数据结构 | 排序问题之快速排序

快速排序

Posted by Aiden on April 14, 2018

思想:

“快速排序”的思想很简单,整个排序过程只需要三步:

(1)在数据集之中,选择一个元素作为"基准"(pivot)。
(2)所有小于"基准"的元素,都移到"基准"的左边;所有大于"基准"的元素,都移到"基准"的右边。
(3)对"基准"左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。

举例来说,现在有一个数据集${85, 24, 63, 45, 17, 31, 96, 50}$,怎么对其排序呢?

第一步,选择中间的元素45作为”基准”。(基准值可以任意选择,但是选择中间的值比较容易理解。)

image.png

第二步,按照顺序,将每个元素与”基准”进行比较,形成两个子集,一个”小于45“,另一个”大于等于45“。

image.png

第三步,对两个子集不断重复第一步和第二步,直到所有子集只剩下一个元素为止。

image.png

快速排序算法 QuickSort

void QuickSort(SeqList R,int low,int high)  
 { //对R[low..high]快速排序  
   int pivotpos; //划分后的基准记录的位置  
   if(low<high){//仅当区间长度大于1时才须排序  
      pivotpos=Partition(R,low,high); //对R[low..high]做划分  
      QuickSort(R,low,pivotpos-1); //对左区间递归排序  
      QuickSort(R,pivotpos+1,high); //对右区间递归排序  
    }  
  } //QuickSort  

划分算法 Partition

具体做法:

(初始化)设置两个指针 $i$ 和 $j$,它们的初值分别为区间的下界和上界,即 $i=low$,$j=high$; 选取无序区的第一个记录 Ri作为基准记录,并将它保存在变量 pivot 中;

令 j 自 high 起向左扫描,直到找到第 1 个关键字小于 pivot.key 的记录 R[j],将 R[j])移至 i 所指的位置上,这相当于 R[j] 和基准 Ri进行了交换,使关键字小于基准关键字 pivot.key 的记录移到了基准的左边,交换后 R[j]中相当于是 pivot;

然后,令 i 指针自 i+1 位置开始向右扫描,直至找到第 1 个关键字大于 pivot.key 的记录 R[i],将 R[i]移到 j 所指的位置上,这相当于交换了 R[i] 和基准 R[j],使关键字大于基准关键字的记录移到了基准的右边,交换后 R[i] 中又相当于存放了 pivot;

接着令指针 j 自位置 j-1 开始向左扫描,如此交替改变扫描方向,从两端各自往中间靠拢,直至 $i=j$ 时,i便是基准 pivot 最终的位置,将 pivot 放在此位置上就完成了一次划分。

int Partition(SeqList R,int i,int j)  
    {//调用Partition(R,low,high)时,对R[low..high]做划分,  
     //并返回基准记录的位置  
      ReceType pivot=R[i]; //用区间的第1个记录作为基准 '  
      while(i<j){ //从区间两端交替向中间扫描,直至i=j为止  
        while(i<j&&R[j].key>=pivot.key) //pivot相当于在位置i上  
          j--; //从右向左扫描,查找第1个关键字小于pivot.key的记录R[j]  
        if(i<j) //表示找到的R[j]的关键字<pivot.key  
            R[i++]=R[j]; //相当于交换R[i]和R[j],交换后i指针加1  
        while(i<j&&R[i].key<=pivot.key) //pivot相当于在位置j上  
            i++; //从左向右扫描,查找第1个关键字大于pivot.key的记录R[i]  
        if(i<j) //表示找到了R[i],使R[i].key>pivot.key  
            R[j--]=R[i]; //相当于交换R[i]和R[j],交换后j指针减1  
       } //endwhile  
      R[i]=pivot; //基准记录已被最后定位  
      return i;  
    } //partition  

冒泡排序

对相邻的元素进行两两比较,顺序相反则进行交换,这样,每一趟会将最小或最大的元素“浮”到顶端,最终达到完全有序。就好像一串气泡一样,最终从小到大或从大到小依次排下来。

image.png

代码实现:

import java.util.Arrays;

public class BubbleSort {
    public static void sort(int[] arr) {
        int len = arr.length;
        int tmp;
        System.out.println("原始顺序: "+ Arrays.toString(arr));
        //i表示第几趟排序
        for (int i = 1; i < len; i++) {
            //每次都从最后一个开始,知道第len-1趟排序
            for (int j = len - 1; j > i-1; j--) {
                //如果后面的比前面的小,就像泡泡一样冒上去
                if (arr[j] < arr[j - 1]) {
                    tmp = arr[j];
                    arr[j] = arr[j - 1];
                    arr[j - 1] = tmp;
                }
            }
            System.out.println("第"+i+"趟排序: "+ Arrays.toString(arr));
        }
    }

    public static void main(String[] args) {
        int[] arr = new int[10];
        //初始化数组
        for (int i = 0; i < 10; i++) {
            arr[i] = (int) (Math.random() * (100 + 1));
        }
        BubbleSort.sort(arr);
    }
}