排序算法总结

1. 冒泡排序

思想

冒泡排序算法的运作如下:
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

代码

void bubbleSort(int arr[], int len) {
     int i, j, temp;
     //外层循环控制次数 内层循环比较
     for (i = 0; i < len - 1; i++) {
          for (j = 0; j < len - 1 - i; j++) {
               if (arr[j] > arr[j + 1]) {
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
               }
          }
    }
}

2. 选择排序

思想

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

代码

void selectionSort(int arr[], int len) {
     int i, j, min, temp;
     for (i = 0; i < len - 1; i++) {
          min = i;
          for (j = i + 1; j < len; j++) {
               if (arr[min] > arr[j])
                    min = j;
          }
          if (min != i) {
               temp = arr[min];
               arr[min] = arr[i];
               arr[i] = temp;
          }
     }
}

3. 插入排序

思想

1.插入排序
一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:
1). 从第一个元素开始,该元素可以认为已经被排序
2). 取出下一个元素,在已经排序的元素序列中从后向前扫描
3). 如果该元素(已排序)大于新元素,将该元素移到下一位置
4). 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5). 将新元素插入到该位置后
6). 重复步骤2~5

代码

void insertionSort(int array[], int first, int last){
     int i, j, temp;
     for (i = first + 1; i <= last; i++){
          temp = array[i];
          for(j = i - 1; j >= first && array[j] > temp; j--)
               array[ j + 1] = array[ j ];
          array[ j+1] = temp; //被排序数放到正确的位置
     }
}

4. 希尔排序

思想

希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。

代码

 void shellSort(int arr[], int len) {
     int gap, i, j;
     int temp;
     for (gap = len >> 1; gap > 0; gap >>= 1) {
          for (i = gap; i < len; i++) {
               temp = arr[i];
               for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap)
                    arr[j + gap] = arr[j];
               arr[j + gap] = temp;
          }
     }
}

5. 归并排序

思想

  1. 从下往上的归并排序:将待排序的数列分成若干个长度为1的子数列,然后将这些数列两两合并;得到若干个长度为2的有序数列,再将这些数列两两合并;得到若干个长度为4的有序数列,再将它们两两合并;直接合并成一个数列为止。这样就得到了我们想要的排序结果。(参考下面的图片)

  2. 从上往下的归并排序:它与”从下往上”在排序上是反方向的。它基本包括3步:
    ① 分解 – 将当前区间一分为二,即求分裂点 mid = (low + high)/2;
    ② 求解 – 递归地对两个子区间a[low…mid] 和 a[mid+1…high]进行归并排序。递归的终结条件是子区间长度为1。
    ③ 合并 – 将已排序的两个子区间a[low…mid]和 a[mid+1…high]归并为一个有序的区间a[low…high]。
    排序算法总结_第1张图片

代码

//从上到下
void mergeSortUp2Down(int a[], int start, int end) {
     if(a == NULL || start >= end)
          return ;
     int mid = (start + end) / 2;
     mergeSortUp2Down(a, start, mid);
     mergeSortUp2Down(a, mid+1, end);

     merge(a, start, mid, end);
}

void merge(int a[], int start, int mid, int end) {
     int* tmp = (int*)malloc((end-start+1)*sizeof(int));
     int i = start;
     int j = mid + 1;
     int k = 0;
     while(i <= mid && j <= end) {
          if(a[i] <= a[j])
               tmp[k++] = a[i++];
          else
               tmp[k++] = a[j++];
     }
     while(i <= mid)
           tmp[k++] = a[i++];
     while(j <= end)
           tmp[k++] = a[j++];
     for(i = 0; i < k ; i++)
          a[start + i] = temp[i];
     free(tmp);
}

//从下到上
void mergeSortDown2Up(int a[], int n) {
     int first, mid , last;
     int i = 0;
     for(int step = 1; step < n; step*=2) {
          for(i = 0; i < n; i += step*2) {
               first = i;
               mid = i + step -1;
               last = i + step*2 - 1;
               // 1 2 3 4 5 step=2的情况
               if(mid >= n)
                    break;
               if(last >= n)
                    last = n-1;
               //子数组进行合并
               merge(a, first, mid, last)
          }
     }
}   

6. 堆排序

思想

最大堆进行升序排序的基本思想:
① 初始化堆:将数列a[1…n]构造成最大堆。
② 交换数据:将a[1]和a[n]交换,使a[n]是a[1…n]中的最大值;然后将a[1…n-1]重新调整为最大堆。 接着,将a[1]和a[n-1]交换,使a[n-1]是a[1…n-1]中的最大值;然后将a[1…n-2]重新调整为最大值。 依次类推,直到整个数列都是有序的。

代码

void headSort(int a[], int length) {
     int i;
     //构造一个堆
     for(i = length/2; i > 0; i--)
          headAdjust(a, i, length);

     for(i = length; i > 1; i--) {
          //交换两个值
          swap(a, 1, i);
          //重新调整
          headAdjust(a, 1, i-1);
     }
}
// s表示需要调整的索引号 m 表示长度
void headAdjust(int a[], int s, int m) {
     int temp, j;
     temp = a[s];
     for(j =2*s; j <= m; j*=2) {
          //相邻两个数的比较
          if(j < m && a[j] < a[j+1])
               j++;
          if(temp >= a[j])
               break;
          a[s] = a[j]
          s = j;
     }
     a[s] = temp;
}

7. 快速排序

思想

  1. 选择一个基准数,通过一趟排序将要排序的数据分割成独立的两部分;其中一部分的所有数据都比另外一部分的所有数据都要小。然后,再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

  2. (1)选择基准:在待排序列中,按照某种方式挑出一个元素,作为 “基准”(pivot)
    (2)分割操作:以该基准在序列中的实际位置,把序列分成两个子序列。此时,在基准左边的元素都比该基准小,在基准右边的元素都比基准大
    (3)递归地对两个序列进行快速排序,直到序列为空或者只有一个元素。

代码

// 版本1 差别在于基准值的选择
void quickSort(int a[], int left, int right) {
     int i ;
     if(left < high) {
          i = partition(a, left, high);
          quickSort(a, left, i-1);
          quickSort(a, i+1, right);
     }
}

int partition(int a[], int left, int right) {
     int i = left;
     int j = right;
     //选取最左边的作为基准值
     int pivot = a[i];
     while(i < j) {
          //从右边找小的
          while(i < j && a[j] >= pivot)
                j--;
          if(i < j)
               a[i++] = a[j];
          //左边找大的
          while(i < j && a[i] <= pivot)
               i++;
          if(i < j)
               a[j--] = a[i];
     }
     a[i] = pivot;
     return i;
}

//优化算法
//1.随机选取轴值
int selectPivotRandom(int arr[],int low,int high) { 
    //产生枢轴的位置 
    srand((unsigned)time(NULL)); 

    int pivotPos = rand()%(high - low + 1) + low; 

    //把枢轴位置的元素和low位置元素互换,此时可以和普通的快排一样调用划分函数 
    swap(arr[pivotPos],arr[low]); 
    return arr[low];
} 
//2. 三个选中法
int selectPivotMedianOfThree(int arr[],int low,int high) { 
    int mid = low + ((high - low) >> 1);//计算数组中间的元素的下标 

    //使用三数取中法选择枢轴 
    if (arr[mid] > arr[high])//目标: arr[mid] <= arr[high] 
    { 
        swap(arr[mid],arr[high]); 
    } 
    if (arr[low] > arr[high])//目标: arr[low] <= arr[high] 
    { 
        swap(arr[low],arr[high]); 
    } 
    if (arr[mid] > arr[low]) //目标: arr[low] >= arr[mid] 
    { 
        swap(arr[mid],arr[low]); 
    } 
    //此时,arr[mid] <= arr[low] <= arr[high] 
    return arr[low]; 
} 

//3. 当待排序序列的长度分割到一定长度(5-20)时,使用插入排序
/*4. 在一次分割结束时,可以把与key相等的元素聚在一起,继续下次分割时,不用再对与key相等的元素分隔。 具体方法: 第一,在划分的过程中,把与key相等元素放入数组的两端 第二,划分结束时,把与key相等的元素移到轴周围 */

//代码如下 举例待排序序列:1 4 6 7 6 6 7 6 8 6
void qSort(int arr[],int low,int high)  { 
    int first = low; 
    int last = high; 

    //把与轴值相等的元素放到两边
    int left = low; 
    int right = high; 

    //与轴值相等的左右两端的长度
    int leftLen = 0; 
    int rightLen = 0; 

    if (high - low + 1 < 10) { 
        InsertSort(arr,low,high); 
        return; 
    } 

    //一次分割 
    int key = selectPivotMedianOfThree(arr,low,high);//使用三数取中法选择枢轴 

    while(low < high) { 
        while(low < high && arr[high] >= key) { 
            if (arr[high] == key)//处理相等元素 { 
                swap(arr[right],arr[high]); 
                right--; 
                rightLen++; 
            } 
            high--; 
        }  
        if(low < high)
             arr[low++] = arr[high]; 
        while(low < high && arr[low] <= key) { 
            if (arr[low] == key) { 
                swap(arr[left],arr[low]); 
                left++; 
                leftLen++; 
            } 
            low++; 
        }  
        if(low < high)
             arr[high--] = arr[low]; 
    } 
    arr[low] = key; 

    //一次快排结束 
    //把与枢轴key相同的元素移到枢轴最终位置周围 
    int i = low - 1; 
    int j = first; 
    while(j < left && arr[i] != key) { 
        swap(arr[i],arr[j]); 
        i--; 
        j++; 
    } 
    i = low + 1; 
    j = last; 
    while(j > right && arr[i] != key) { 
        swap(arr[i],arr[j]); 
        i++; 
        j--; 
    } 
    qSort(arr,first,low - 1 - leftLen); 
    qSort(arr,low + 1 + rightLen,last); 
}   



8. 二分查找

思想

是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。

代码

// mid = (low + high) / 2 = low + 1/2 (high - low)
int binarySearch(int a[], int low, int high, int key) {
   int mid;
   while(low <= high) {
          mid = (low + high) / 2;
          if(key == a[mid])
               return mid;
          else if(key > a[mid])
               low = mid + 1;
          else
               high = mid - 1;
    }
    return -1;
}

你可能感兴趣的:(排序算法)