zoukankan      html  css  js  c++  java
  • java常用排序算法

    一、冒泡排序

      1、基本介绍

      冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。

    因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置
    一个标志flag判断元素是否进行过交换。从而减少不必要的比较。(这里说的优化,可以在冒泡排序写好后,在进行)
      例如:

      
      
      

      2、代码实现

      

     1     //基础版
     2     public static void bubbleSort_v1(int[] arr) {
     3         int tmp=0;
     4         for (int i = 0; i < arr.length - 1; i++) {
     5             for (int j = 0; j < arr.length - 1 - i; j++) {
     6                 if (arr[j] > arr[j + 1]) {
     7                     tmp = arr[j];
     8                     arr[j] = arr[j + 1];
     9                     arr[j + 1] = tmp;
    10                 }
    11             }
    12         }
    13     }

     

     1  //加强版
     2     public static void bubbleSort_v2(int[] arr) {
     3         boolean flag = false;
     4         int tmp=0;
     5         for (int i = 0; i < arr.length - 1; i++) {
     6             for (int j = 0; j < arr.length - 1 - i; j++) {
     7                 if (arr[j] > arr[j + 1]) {
     8                     flag=true;
     9                     tmp = arr[j];
    10                     arr[j] = arr[j + 1];
    11                     arr[j + 1] = tmp;
    12                 }
    13             }
    14             //如果没进入比较,则表示已经是有序数组,中断排序即可
    15             if(!flag){
    16                 break;
    17             }else {
    18                 flag=false;
    19             }
    20         }
    21     }

    二、快速排序

      

      1、基本介绍 

      快速排序(Quicksort)是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

      

      

      

      2、代码实现

      

     1 public class QuickSort1 {
     2     // 我们的算法类不允许产生任何实例
     3     private  QuickSort1(){}
     4     // 对arr[low...high]部分进行partition操作
     5     // 返回p, 使得arr[low...p-1] < arr[p] ; arr[p+1...high] > arr[p]
     6     private static int partition(int[] arr, int low, int high){
     7         // 随机在arr[l...r]的范围中, 选择一个数值作为标定点pivot
     8         Common.swap(arr,low,(int)(Math.random() * (high - low + 1)) + 1);
     9 
    10         int v = arr[low];
    11         int j = low;// arr[l+1...j] < v ; arr[j+1...i) > v
    12         for (int i =low+1; i <=high ; i++) {
    13             if(arr[i]<v){
    14                 j++;
    15                 Common.swap(arr,i,j);
    16             }
    17         }
    18         Common.swap(arr,low,j);
    19         return j;
    20     }
    21     // 递归使用快速排序,对arr[l...r]的范围进行排序
    22     private static void sort(int[] arr, int low, int high){
    23         if( low >= high )
    24             return;
    25         int p=partition(arr,low,high);
    26         sort(arr,low,p-1);
    27         sort(arr,p+1,high);
    28     }
    29     public static  void sort(int[] arr){
    30         int n = arr.length;
    31         sort(arr,0,n-1);
    32     }
    33 }

       

     1 package com.atguigu.Sort.QuickSort;
     2 
     3 import com.atguigu.Sort.Common;
     4 
     5 /**
     6  * @author z
     7  * @createdate 2019-08-06 9:02
     8  */
     9 public class QuickSort2 {
    10     // 我们的算法类不允许产生任何实例
    11     private QuickSort2() {
    12     }
    13 
    14     // 对arr[l...r]部分进行partition操作
    15     // 返回p, 使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p]
    16     private static int partition(int[] arr, int l, int r) {
    17         // 随机在arr[l...r]的范围中, 选择一个数值作为标定点pivot
    18         Common.swap(arr, l, (int) (Math.random() * (r - l + 1)) + 1);
    19 
    20         int v = arr[l];
    21         int i = l + 1, j = r; // arr[l+1...i) <= v; arr(j...r] >= v
    22         while (true) {
    23             // 注意这里的边界, arr[i].compareTo(v) < 0, 不能是arr[i].compareTo(v) <= 0
    24             // 思考一下为什么?
    25             while (i <= r && arr[i] < v) i++;
    26             // 注意这里的边界, arr[j].compareTo(v) > 0, 不能是arr[j].compareTo(v) >= 0
    27             // 思考一下为什么?
    28             while (j >= l + 1 && arr[j] > v) j--;
    29             // 对于上面的两个边界的设定, 有的同学在课程的问答区有很好的回答:)
    30             // 大家可以参考: http://coding.imooc.com/learn/questiondetail/4920.html
    31             if (i > j) break;
    32             Common.swap(arr, i, j);
    33             i++;
    34             j--;
    35         }
    36         Common.swap(arr, l, j);
    37         return j;
    38     }
    39 
    40     // 递归使用快速排序,对arr[l...r]的范围进行排序
    41     private static void sort(int[] arr, int l, int r) {
    42         if (l >= r)
    43             return;
    44         int p = partition(arr, l, r);
    45         sort(arr, l, p - 1);
    46         sort(arr, p + 1, r);
    47     }
    48 
    49     public static void sort(int[] arr) {
    50         int n = arr.length;
    51         sort(arr, 0, n - 1);
    52     }
    53 }

    三、直接插入排序

       

      1、基本介绍 

      插入排序(Insertion Sorting)的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表

      

      

      2、代码实现

       

     1  public  static void insertSort_v1(int[] arr){
     2         //从第二个元素开始,因为如果从第一个元素向前插入,第一个元素之前是没有数据的
     3         for (int i = 1; i < arr.length; i++) {
     4             //从第i个元素开始向前查找正确的插入位置
     5             //下标j>0并且当前元素值arr[j]小于arr[j-1]时,进入循环,即从小到大
     6             for (int j = i; j > 0 && arr[j] < arr[j - 1]; j--) {
     7                 if (arr[j] < arr[j - 1]) {
     8                     int tmp = arr[j];
     9                     arr[j] = arr[j - 1];
    10                     arr[j - 1] = tmp;
    11                 }
    12             }
    13 
    14         }
    15     }

      

     1  public  static void insertSort_v2(int[] arr){
     2         for (int i = 1; i < arr.length; i++) {
     3             // 寻找元素arr[i]合适的插入位置
     4             int e = arr[i];
     5             int j = 0;
     6             // j保存元素e应该插入的位置,一次赋值,交换一次需要三次赋值
     7             for (j = i; j > 0 && arr[j - 1] > e; j--) {
     8                 //此时arr[j],arr[j-1]是同一个数
     9                 arr[j] = arr[j - 1];
    10             }
    11             //j是arr[i]应该插入的位置
    12             arr[j] = e;
    13         }
    14     }

    3.1、折半插入排序

      

      1、基本介绍 

      2、代码实现

    四、希尔排序

      

      1、基本介绍 

      

      

      

      2、演化

      

     1  //演化
     2     private void evolution(){
     3         int[] arr = { 8, 9, 1, 7, 2, 3, 5, 4, 6, 0 };
     4         int temp = 0;
     5         // 希尔排序的第1轮排序
     6         // 因为第1轮排序,是将10个数据分成了 5组
     7         for (int i = 5; i < arr.length; i++) {
     8             // 遍历各组中所有的元素(共5组,每组有2个元素), 步长5
     9             for (int j = i - 5; j >= 0; j -= 5) {
    10                 // 如果当前元素大于加上步长后的那个元素,说明交换
    11                 if (arr[j] > arr[j + 5]) {
    12                     temp = arr[j];
    13                     arr[j] = arr[j + 5];
    14                     arr[j + 5] = temp;
    15                 }
    16             }
    17         }
    18 
    19         System.out.println("希尔排序1轮后=" + Arrays.toString(arr));//
    20 
    21 
    22         // 希尔排序的第2轮排序
    23         // 因为第2轮排序,是将10个数据分成了 5/2 = 2组
    24         for (int i = 2; i < arr.length; i++) {
    25             // 遍历各组中所有的元素(共5组,每组有2个元素), 步长5
    26             for (int j = i - 2; j >= 0; j -= 2) {
    27                 // 如果当前元素大于加上步长后的那个元素,说明交换
    28                 if (arr[j] > arr[j + 2]) {
    29                     temp = arr[j];
    30                     arr[j] = arr[j + 2];
    31                     arr[j + 2] = temp;
    32                 }
    33             }
    34         }
    35 
    36         System.out.println("希尔排序2轮后=" + Arrays.toString(arr));//
    37 
    38         // 希尔排序的第3轮排序
    39         // 因为第3轮排序,是将10个数据分成了 2/2 = 1组
    40         for (int i = 1; i < arr.length; i++) {
    41             // 遍历各组中所有的元素(共5组,每组有2个元素), 步长5
    42             for (int j = i - 1; j >= 0; j -= 1) {
    43                 // 如果当前元素大于加上步长后的那个元素,说明交换
    44                 if (arr[j] > arr[j + 1]) {
    45                     temp = arr[j];
    46                     arr[j] = arr[j + 1];
    47                     arr[j + 1] = temp;
    48                 }
    49             }
    50         }
    51 
    52         System.out.println("希尔排序3轮后=" + Arrays.toString(arr));//
    53     }

      3、代码实现

      

     1  // 希尔排序时, 对有序序列在插入时采用->交换法
     2     public static void sellSort_v1(int [] arr){
     3         int tmp = 0;
     4         for (int gap =arr.length/2 ; gap >0 ; gap/=2) {
     5             for (int i = gap; i <arr.length ; i++) {
     6                 // 遍历各组中所有的元素(共gap组,每组有个元素), 步长gap
     7                 for (int j = i-gap; j >=0; j-=gap) {
     8                     // 如果当前元素大于加上步长后的那个元素,说明交换
     9                     if(arr[j]<arr[j+gap]){
    10                         tmp= arr[j];
    11                         arr[j]=arr[j+gap];
    12                         arr[j+gap]=tmp;
    13                     }
    14                 }
    15             }
    16         }
    17     }

       

     1  //对交换式的希尔排序进行优化->移位法
     2     public static void sellSort_v2(int [] arr){
     3         for (int gap =arr.length/2 ; gap >0 ; gap/=2) {
     4             // 从第gap个元素,逐个对其所在的组进行直接插入排序
     5             for (int i = gap; i < arr.length; i++) {
     6                 int j = i;
     7                 int tmp = arr[j];
     8                 if(arr[j]<arr[j-gap]){
     9                     while (j - gap >= 0 && tmp < arr[j - gap]) {
    10                         //移动
    11                         arr[j] = arr[j-gap];
    12                         j -= gap;
    13                     }
    14                     //当退出for后,就给temp找到插入的位置
    15                     arr[j]=tmp;
    16                 }
    17             }
    18         }
    19     }

    五、直接选择排序

      1、基本介绍

      选择排序(select sorting)也是一种简单的排序方法。它的基本思想是:第一次从arr[0]~arr[n-1]中选取最小值,与arr[0]交换,第二次从arr[1]~arr[n-1]中选取最小值,与arr[1]交换,第三次从arr[2]~arr[n-1]中选取最小值,与arr[2]交换,…,第i次从arr[i-1]~arr[n-1]中选取最小值,与arr[i-1]交换,…, 第n-1次从arr[n-2]~arr[n-1]中选取最小值,与arr[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列。

      

      

      2、代码实现

      

     1  //O(n2)
     2     public static void selectSort(int[] arr){
     3         for (int i = 0; i < arr.length-1; i++) {
     4             //假定最小值为i
     5             int minIndex = i;
     6             //j从i+1开始
     7             for (int j = i+1; j <arr.length ; j++) {
     8                 //如果假定的最小值大于后面一个元素即arr[j],则吧minIndex指向arr[j]
     9                 //否则不进入if判断
    10                 if(arr[minIndex]>arr[j]){
    11                     minIndex = j;
    12                 }
    13             }
    14             //如果minIndex没有变化,即,minIndex就是arr[i],不需交换
    15             if(minIndex!=i) {
    16                 int tmp = arr[i];
    17                 arr[i] = arr[minIndex];
    18                 arr[minIndex] = tmp;
    19             }
    20         }
    21     }

    六、堆排序

      1、堆排序基本介绍

      1)堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。
      2)堆是具有以下性质的完全二叉树每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆, 注意 : 没有要求结点的左孩子的值和右孩子的值的大小关系。
      3)每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆
      4)大顶堆举例

      

      我们对堆中的结点按层进行编号,映射到数组中就是下面这个样子:

      

      大顶堆的特点:

      arr[i] >= arr[2*i+1] && arr[i] >= arr[2*i+2]  // i 对应第几个节点,i从0开始编号

      5)小顶堆举例

      

      小顶堆的特点:

      arr[i] <= arr[2*i+1] && arr[i] <= arr[2*i+2] // i 对应第几个节点,i从0开始编号

      6)一般升序采用大顶堆,降序采用小顶堆

      2、堆排序的基本思路

      1)将待排序序列构造成一个大顶堆
      2)此时,整个序列的最大值就是堆顶的根节点。
      3)将其与末尾元素进行交换,此时末尾就为最大值。
      4)然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。

      1、基本介绍 

      

      

      

      2、代码实现

       

     1 public class HeapSort {
     2 
     3     public static void  heapSort(int[] arr) {
     4         int temp= 0;
     5 //        int arr[] = {4, 6, 8, 5, 9};
     6         //将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆
     7         for (int i = arr.length/2-1; i >= 0; i--) {
     8             adjustHeap(arr,i,arr.length);
     9         }
    10         //System.out.println(Arrays.toString(arr));
    11         //2).将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
    12         //3).重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
    13         for (int j = arr.length-1; j >0 ; j--) {
    14             temp = arr[j];
    15             arr[j] = arr[0];
    16             arr[0] = temp;
    17             adjustHeap(arr,0,j);
    18         }
    19         //System.out.println(Arrays.toString(arr));
    20     }
    21 
    22     //将一个数组(二叉树), 调整成一个大顶堆
    23     /**
    24      * 功能: 完成 将 以 i 对应的非叶子结点的树调整成大顶堆
    25      * 举例  int arr[] = {4, 6, 8, 5, 9}; => i = 1 => adjustHeap => 得到 {4, 9, 8, 5, 6}
    26      * 如果我们再次调用  adjustHeap 传入的是 i = 0 => 得到 {4, 9, 8, 5, 6} => {9,6,8,5, 4}
    27      * @param arr 待调整的数组
    28      * @param i 表示非叶子结点在数组中索引
    29      * @param lenght 表示对多少个元素继续调整, length 是在逐渐的减少
    30      */
    31     private static void adjustHeap(int arr[], int i,int length){
    32         //先取出当前元素的值,保存在临时变量
    33         int temp = arr[i];
    34         //开始调整
    35         //说明
    36         //1. k = i * 2 + 1 k 是 i结点的左子结点
    37         for (int k = i*2+1; k <length; k=i*2+1) {
    38             //说明左子结点的值小于右子结点的值
    39             if(k+1<length && arr[k]<arr[k+1]){
    40                 // k 指向右子结点
    41                 k++;
    42             }
    43             //如果子结点大于父结点
    44             if(arr[k]>temp){
    45                 //把较大的值赋给当前结点
    46                 arr[i] = arr[k];
    47                 //!!! i 指向 k,继续循环比较
    48                 i=k;
    49             }else{
    50                 break;
    51             }
    52         }
    53         //当for 循环结束后,我们已经将以i 为父结点的树的最大值,放在了 最顶(局部)
    54         arr[i] = temp;//将temp值放到调整后的位置
    55     }
    56 }

    七、归并排序

      1、基本介绍 

      归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)

      策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
      

      

      

      2、代码实现

       

     1 public class MergeSort {
     2     public static void sort(int[] arr, int left, int right, int[] temp) {
     3         if (left < right) {
     4             int mid = (left + right) / 2;
     5             //向左递归进行分解
     6             sort(arr, left, mid, temp);
     7             //向右递归进行分解
     8             sort(arr, mid + 1, right, temp);
     9             //合并
    10             merge(arr, left, mid, right, temp);
    11         }
    12     }
    13 
    14     /**
    15      * @param arr   排序的原始数组
    16      * @param left  左边有序序列的初始索引
    17      * @param mid   中间索引
    18      * @param right 右边索引
    19      * @param temp  做中转的数组
    20      */
    21     private static void merge(int[] arr, int left, int mid, int right, int[] temp) {
    22         int i = left; // 初始化i, 左边有序序列的初始索引
    23         int j = mid + 1; //初始化j, 右边有序序列的初始索引
    24         int t = 0; // 指向temp数组的当前索引
    25         //(一)
    26         //先把左右两边(有序)的数据按照规则填充到temp数组
    27         //直到左右两边的有序序列,有一边处理完毕为止
    28         while (i <= mid && j <= right) {
    29             //如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素
    30             //即将左边的当前元素,填充到 temp数组
    31             //然后 t++, i++
    32             if (arr[i] <= arr[j]) {
    33                 temp[t++] = arr[i++];
    34             } else {
    35                 temp[t++] = arr[j++];
    36             }
    37         }
    38 
    39         //(二)
    40         //把有剩余数据的一边的数据依次全部填充到temp
    41         while (i <= mid) {//左边的有序序列还有剩余的元素,就全部填充到temp
    42             temp[t++] = arr[i++];
    43         }
    44         while (j <= right) {//右边的有序序列还有剩余的元素,就全部填充到temp
    45             temp[t++] = arr[j++];
    46         }
    47 
    48         //(三)
    49         //将temp数组的元素拷贝到arr
    50         //注意,并不是每次都拷贝所有
    51         t=0;
    52         int tempLeft=left;
    53         //第一次合并 tempLeft = 0 , right = 1 //  tempLeft = 2  right = 3
    54         //最后一次 tempLeft = 0  right = 7
    55         while (tempLeft<=right){
    56             arr[tempLeft++]=temp[t++];
    57         }
    58     }
    59 }

    八、基数排序

      1、基数排序(桶排序)介绍

      1)基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用

      2)基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法

      3)基数排序(Radix Sort)是桶排序的扩展

      4)基数排序是1887年赫尔曼·何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较

        

        

      2、基数排序基本思想

      1)将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。

       然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。


      2)这样说明,比较难理解,下面我们看一个图文解释,理解基数排序的步骤
      3、基数排序图文说明

      将数组 {53, 3, 542, 748, 14, 214} 使用基数排序, 进行升序排序

      

      

      

      4、基数排序说明

      1)基数排序是对传统桶排序的扩展,速度很快.
      2)基数排序是经典的空间换时间的方式,占用内存很大, 当对海量数据排序时,容易造成 OutOfMemoryError 。(本人电脑1亿随机数排序会出现,1千万不会)
      3)基数排序时稳定的。

      [注:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的]
      4)有负数的数组,我们不用基数排序来进行排序, 如果要支持负数,参考: https://code.i-harness.com/zh-CN/q/e98fa9


      2、代码实现

      

     1 public class RadixSort {
     2     public static void sort(int[] arr){
     3         //1. 得到数组中最大的数的位数
     4         int max = arr[0]; //假设第一数就是最大数
     5         for (int i = 1; i <arr.length ; i++) {
     6             if(arr[i]>max){
     7                 max=arr[i];
     8             }
     9         }
    10         //得到最大数是几位数
    11         int maxlength =(max+"").length();
    12         //定义一个二维数组,表示10个桶, 每个桶就是一个一维数组
    13         //说明
    14         //1. 二维数组包含10个一维数组
    15         //2. 为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr.length
    16         //3. 名明确,基数排序是使用空间换时间的经典算法
    17         int[][] bucket = new int[10][arr.length];
    18         //为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数
    19         //可以这里理解
    20         //比如:bucketElementCounts[0] , 记录的就是  bucket[0] 桶的放入数据个数
    21         int[] bucketElementCounts = new int[10];
    22         for (int i = 0,n=1; i <maxlength ; i++,n*=10) {
    23             //(针对每个元素的对应位进行排序处理), 第一次是个位,第二次是十位,第三次是百位..
    24             for (int j = 0; j <arr.length ; j++) {
    25                 int digitOfElement=arr[j]/n%10;
    26                 bucket[digitOfElement][bucketElementCounts[digitOfElement]]=arr[j];
    27                 bucketElementCounts[digitOfElement]++;
    28             }
    29             //按照这个桶的顺序(一维数组的下标依次取出数据,放入原来数组)
    30             int index =0;
    31             //遍历每一桶,并将桶中是数据,放入到原数组
    32             for (int k = 0; k < bucketElementCounts.length; k++) {
    33                 //如果桶中,有数据,我们才放入到原数组
    34                 if(bucketElementCounts[k]!=0){
    35                     //循环该桶即第k个桶(即第k个一维数组), 放入
    36                     for (int l = 0; l < bucketElementCounts[k]; l++) {
    37                         arr[index++]=bucket[k][l];
    38                     }
    39                 }
    40                 //第i+1轮处理后,需要将每个 bucketElementCounts[k] = 0 !!!!
    41                 bucketElementCounts[k]=0;
    42             }
    43            // System.out.println(Arrays.toString(arr));
    44         }
    45 
    46     }
    47 }

     

    九、桶排序

      1、基本介绍 

      

      2、代码实现

    十、计数排序

      1、基本介绍

      

      2、代码实现

  • 相关阅读:
    BNU 51002 BQG's Complexity Analysis
    BNU OJ 51003 BQG's Confusing Sequence
    BNU OJ 51000 BQG's Random String
    BNU OJ 50999 BQG's Approaching Deadline
    BNU OJ 50998 BQG's Messy Code
    BNU OJ 50997 BQG's Programming Contest
    CodeForces 609D Gadgets for dollars and pounds
    CodeForces 609C Load Balancing
    CodeForces 609B The Best Gift
    CodeForces 609A USB Flash Drives
  • 原文地址:https://www.cnblogs.com/hyunbar/p/11304882.html
Copyright © 2011-2022 走看看