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. 桶排序
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值