LintCode 463: Sort Integers (QuickSort和MergeSort 经典模板!!!)

  1. Sort Integers
    中文English
    Given an integer array, sort it in ascending order. Use selection sort, bubble sort, insertion sort or any O(n2) algorithm.

Example
Example 1:
Input: [3, 2, 1, 4, 5]
Output: [1, 2, 3, 4, 5]

Explanation: 
return the sorted array.

Example 2:
Input: [1, 1, 2, 1, 1]
Output: [1, 1, 1, 1, 2]

Explanation: 
return the sorted array.

解法1:
QuickSort经典模板。
注意:

  1. pivot是取值而不是取index,因为QuickSort过程中元素会交换,index会变。
  2. quickSort()刚进来要备份left=start, right=end,因为start和end会变。
  3. 所有比较left和right的地方都必须用<=,所有比较A[left]/A[right]和pivot的地方都必须用<。否则可能溢出,因为递归的时候start和end老是不变。
    代码如下。
  4. quickSort()里面一开始if(start>=end) return这里可以用>=也可以用>。但MergeSort()这个地方只能用>=。所以为了统一,这里都用>=。
class Solution {
public:
    /**
     * @param A: an integer array
     * @return: nothing
     */
    void sortIntegers(vector<int> &A) {
        int n = A.size();
        if (n == 0) return;
        
        quickSort(A, 0, n - 1);
    }
    
private:
    void quickSort(vector<int> &A, int start, int end) {
        if (start >= end) return; //这里也可以用>
        int left = start, right = end;
        int pivot = A[left + (right - left) / 2];
        
        while(left <= right) {
            while(left <= right && A[left] < pivot) { // 注意这里要写pivot,而不是A[left + (right - left) / 2]; 因为A[left + (right - left) / 2]值可能会变!!!
                left++;
            }    
            while(left <= right && A[right] > pivot) { // 注意这里要写pivot,而不是A[left + (right - left) / 2]; 因为A[left + (right - left) / 2]值可能会变!!!
                right--;
            }
            if (left <= right) {
                swap(A[left], A[right]);
                left++;
                right--;
            }
        }
        
        quickSort(A, start, right);
        quickSort(A, left, end);
    }
};

另一个quickSort的解法,是把结尾元素当成pivot,每次quickSort执行后,start…i-1之间是小于pivot的,i等于pivot,i+1…end是大于pivot的。有点类似选择排序,不同点是选择排序每次是在i+1到end间选一个最小值跟a[i]交换,而这里是在i+1到end间找到第一个大于pivot的值就跟a[i]交换。

class Solution {
public:
    /**
     * @param a: an integer array
     * @return: nothing
     */
    void sortIntegers(vector<int> &a) {
        int n = a.size();
        quickSort(a, 0, n - 1);
    }
private:
    void quickSort(vector<int> &a, int start, int end) {
        if (start >= end) return;
        int i = start;
        for (int j = start; j < end; j++) {
            if (a[j] < a[end]) {
                swap(a[i], a[j]);
                i++;
            }
        }
        swap(a[i], a[end]);

        quickSort(a, start, i - 1);
        quickSort(a, i + 1, end);
    }    
};

解法2:MergeSort经典模板。
注意:

  1. mergeSort()刚进去的地方if(start>=end) continue;这里只能用>=, 和quickSort()不同。
  2. mergeSort() 外面一定要开辟一个n长度的数组,这是mergeSort比quickSort费事的地方,因为开辟一个区间(之后还要回收)很费时间。
  3. mergeSort()进去了start和end都要备份。记得这里right=mid+1 (quickSort是right=end)。
  4. 所有的比较都用<=
  5. merge()里面最后记得把buf[]的start…end元素拷贝到A[]里面(不能直接A=buf)。
  6. mid是取index(不是取值!)。
  7. 简而言之,mergeSort的代码结构是while-if-while-while-for,而quickSort是while-{while-while}-if。
    代码如下:
class Solution {
public:
    /**
     * @param A: an integer array
     * @return: nothing
     */
    void sortIntegers(vector<int> &A) {
        int n = A.size();
        if (n == 0) return;
        vector<int> temp(n, 0);
        mergeSort(A, 0, n - 1, temp);
    }
    
private:
    void mergeSort(vector<int> &A, int start, int end, vector<int> &buf) {
        if (start >= end) return;//这里只能用>=
        int mid = start + (end - start) / 2;
        mergeSort(A, start, mid, buf);
        mergeSort(A, mid + 1, end, buf);
        merge(A, start, end, buf);
    }
    
    void merge(vector<int> &A, int start, int end, vector<int> &buf) {
        int mid = start + (end - start) / 2;
        int left = start;
        int right = mid + 1;
        int index = left;
        
        while(left <= mid && right <= end) {
            if (A[left] <= A[right]) {
                buf[index++] = A[left++];
            } else {
                buf[index++] = A[right++];
            }
        }
        
        while(left <= mid) {
            buf[index++] = A[left++];
        }
        
        while(right <= end) {
            buf[index++] = A[right++];
        }
        
        //remember to copy buf back to A
        //cannot directly use A = buf!
        for (int i = start; i <= end; ++i) {
            A[i] = buf[i];
        }
    }
};

下面是其它一些排序算法的代码:

  1. 冒泡排序
void bubble_sort(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;
            }
        }
    }
}

  1. 插入排序
class Solution {
public:
    /**
     * @param A: an integer array
     * @return: nothing
     */
    void sortIntegers(vector<int> &A) {
        int sizeA = A.size();
        if (sizeA == 0) return;
        insertionSort(A);
        return;
    }
private:
    void insertionSort(vector<int> &A) {
        int len = A.size();
        for (int i = 1; i < len; i++) {
            int j = i;
            while (j > 0) {
                if (A[j] < A[j - 1]) {
                    int tmp = A[j];
                    A[j] = A[j - 1];
                    A[j - 1] = tmp;
                    j--;
                } else {
                    break;
                }
            }
        }
    }
};

注意:上面的版本是错的!!!这个版本实际上是冒泡排序。插入排序不需要swap。
新版本如下:

class Solution {
public:
    /**
     * @param a: an integer array
     * @return: nothing
     */
    void sortIntegers(vector<int> &a) {
        //insertion sort  
        int len = a.size();
        for (int i = 0; i < len; i++) {
            int value = a[i];
            int pos = 0;
            for (int j = i - 1; j >= 0; j--) {
                if (a[j] > value) {
                    a[j + 1] = a[j];
                } else {
                    pos = j + 1;
                    break;
                }
            } 
            a[pos] = value; 
        }
    }
};

注意,冒泡排序和插入排序时间复杂度都是O(n^2),为什么还是插入排序更受欢迎呢?因为冒泡排序需要swap,所以每次交换有至少3个赋值操作。而插入排序每次只有a[j+1]=a[j]这一个赋值操作。

  1. 选择排序
class Solution {
public:
    /**
     * @param A: an integer array
     * @return: nothing
     */
    void sortIntegers(vector<int> &A) {
        int sizeA = A.size();
        if (sizeA == 0) return;
        selectionSort(A);
        return;
    }
private:
    void selectionSort(vector<int> &A) {
        int len = A.size();
        for (int i = 0; i < len - 1; i++) {
            int minIndex = i;
            for (int j = i + 1; j < len; j++) {
                if (A[minIndex] > A[j]) minIndex = j;
            }
            int tmp = A[minIndex];
            A[minIndex] = A[i];
            A[i] = tmp;
        }
        return;
    }
};

注意:直接选择排序算法是不稳定的,举个简单的例子,就知道它是否稳定…例如:(7) 2 5 9 3 4 [7] 1…当我们利用直接选择排序算法进行排序时候,(7)和1调换,(7)就跑到了[7]的后面了,原来的次序改变了,这样就不稳定了。
总结,有swap的排序都不稳定。

  1. Shell排序
  2. Heap排序
  3. Radix排序
  4. 桶排序
#include <stdio.h> // 交换两个元素的值 void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } // 冒泡排序 void bubbleSort(int arr[], int n) { for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - i - 1; j++) { if (arr[j] > arr[j + 1]) { swap(&arr[j], &arr[j + 1]); } } } } // 选择排序 void selectionSort(int arr[], int n) { for (int i = 0; i < n - 1; i++) { int minIndex = i; for (int j = i + 1; j < n; j++) { if (arr[j] < arr[minIndex]) { minIndex = j; } } swap(&arr[minIndex], &arr[i]); } } // 插入排序 void insertionSort(int arr[], int n) { for (int i = 1; i < n; i++) { int key = arr[i]; int j = i - 1; while (j >= 0 && arr[j] > key) { arr[j + 1] = arr[j]; j--; } arr[j + 1] = key; } } // 希尔排序 void shellSort(int arr[], int n) { for (int gap = n / 2; gap > 0; gap /= 2) { for (int i = gap; i < n; i++) { int temp = arr[i]; int j; for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) { arr[j] = arr[j - gap]; } arr[j] = temp; } } } // 快速排序的分区函数 int partition(int arr[], int low, int high) { int pivot = arr[high]; int i = low - 1; for (int j = low; j < high; j++) { if (arr[j] < pivot) { i++; swap(&arr[i], &arr[j]); } } swap(&arr[i + 1], &arr[high]); return i + 1; } // 快速排序 void quickSort(int arr[], int low, int high) { if (low < high) { int pi = partition(arr, low, high); quickSort(arr, low, pi - 1); quickSort(arr, pi + 1, high); } } // 归并排序的合并函数 void merge(int arr[], int l, int m, int r) { int n1 = m - l + 1; int n2 = r - m; int L[n1], R[n2]; for (int i = 0; i < n1; i++) { L[i] = arr[l + i]; } for (int j = 0; j < n2; j++) { R[j] = arr[m + 1 + j]; } int i = 0, j = 0, k = l; while (i < n1 && j < n2) { if (L[i] <= R[j]) { arr[k] = L[i]; i++; } else { arr[k] = R[j]; j++; } k++; } while (i < n1) { arr[k] = L[i]; i++; k++; } while (j < n2) { arr[k] = R[j]; j++; k++; } } // 归并排序 void mergeSort(int arr[], int l, int r) { if (l < r) { int m = l + (r - l) / 2; mergeSort(arr, l, m); mergeSort(arr, m + 1, r); merge(arr, l, m, r); } } // 打印数组 void printArray(int arr[], int n) { for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); } int main() { int arr[] = {12, 11, 13, 5, 6, 7}; int n = sizeof(arr) / sizeof(arr[0]); printf("Original array: "); printArray(arr, n); // 冒泡排序 int arr1[n]; for (int i = 0; i < n; i++) { arr1[i] = arr[i]; } bubbleSort(arr1, n); printf("Bubble sorted array: "); printArray(arr1, n); // 选择排序 int arr2[n]; for (int i = 0; i < n; i++) { arr2[i] = arr[i]; } selectionSort(arr2, n); printf("Selection sorted array: "); printArray(arr2, n); // 插入排序 int arr3[n]; for (int i = 0; i < n; i++) { arr3[i] = arr[i]; } insertionSort(arr3, n); printf("Insertion sorted array: "); printArray(arr3, n); // 希尔排序 int arr4[n]; for (int i = 0; i < n; i++) { arr4[i] = arr[i]; } shellSort(arr4, n); printf("Shell sorted array: "); printArray(arr4, n); // 快速排序 int arr5[n]; for (int i = 0; i < n; i++) { arr5[i] = arr[i]; } quickSort(arr5, 0, n - 1); printf("Quick sorted array: "); printArray(arr5, n); // 归并排序 int arr6[n]; for (int i = 0; i < n; i++) { arr6[i] = arr[i]; } mergeSort(arr6, 0, n - 1); printf("Merge sorted array: "); printArray(arr6, n); return 0; } 根据这个代码重新生成生500、10000、50000、100000 四个量级的随机整型关键字,按 “每行一个” 规范写入文件,完成正整数输入校验及文件打开、内存分配等异常处理,适配 VS2022 Preview 环境的代码
最新发布
12-11
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值