赞
踩
最近为了熟悉经典的排序算法,我又进行了复习总结,实现过程全部由C++完成。代码结构清晰简洁,方便学习和复习,也是我们必须掌握的基础。
1、稳定排序:如果 a 原本在 b 的前面,且 a == b,排序之后 a 仍然在 b 的前面,则为稳定排序。
2、非稳定排序:如果 a 原本在 b 的前面,且 a == b,排序之后 a 可能不在 b 的前面,则为非稳定排序。
3、原地排序:原地排序就是指在排序过程中不申请多余的存储空间,只利用原来存储待排数据的存储空间进行比较和交换的数据排序。
4、非原地排序:需要利用额外的数组来辅助排序。
5、时间复杂度:一个算法执行所消耗的时间。
6、空间复杂度:运行完一个算法所需的内存大小
冒泡排序就是把小的元素往前调或者把大的元素往后调,比较是相邻的两个元素,再进行交换。
算法过程
选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给>二个元素选择第二小的,依次类推,直到第n-1个元素,第n个 元素不用选择了,因为只剩下它一个最大的元素了。
算法过程
插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。当然,刚开始这个有序的小序列只有1个元素,就是第一个元素。比较是从有序序列的末尾开 始,也就是想要插入的元素和已经有序的最大者开始比起,如果比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。
算法过程
1.从第一个元素开始,该元素可以认为已经被排序
2.取出下一个元素,在已经排序的元素序列中从后向前扫描
3.如果该元素(已排序)大于新元素,将该元素移到下一位置
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5.将新元素插入到该位置后
思路
1、选取第一个数为基准
2、将比基准小的数交换到前面,比基准大的数交换到后面
3、对左右区间重复第二步,直到各区间只有一个数
希尔排序可以说是插入排序的一种变种。无论是插入排序还是冒泡排序,如果数组的最大值刚好是在第一位,要将它挪到正确的位置就需要 n - 1 次移动。也就是说,原数组的一个元素如果距离它正确的位置很远的话,则需要与相邻元素交换很多次才能到达正确的位置,这样是相对比较花时间了。
希尔排序就是为了加快速度简单地改进了插入排序,交换不相邻的元素以对数组的局部进行排序。
希尔排序的思想是采用插入排序的方法,先让数组中任意间隔为 h 的元素有序,刚开始 h 的大小可以是 h = n / 2,接着让 h = n / 4,让 h 一直缩小,当 h = 1 时,也就是此时数组中任意间隔为1的元素有序,此时的数组就是有序的了。
将一个大的无序数组有序,我们可以把大的数组分成两个,然后对这两个数组分别进行排序,之后在把这两个数组合并成一个有序的数组。由于两个小的数组都是有序的,所以在合并的时候是很快的。 通过递归的方式将大的数组一直分割,直到数组的大小为 1,此时只有一个元素,那么该数组就是有序的了,之后再把两个数组大小为1的合并成一个大小为2的,再把两个大小为2的合并成4的 … 直到全部小的数组合并起来。
算法思想
1、把长度为n的输入序列分成两个长度为n/2的子序列;
2、对这两个子序列分别采用归并排序;
3、 将两个排序好的子序列合并成一个最终的排序序列。
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
首先堆heap是一种数据结构,是一棵完全二叉树且满足性质:所有非叶子结点的值均不大于或均不小于其左、右孩子结点的值.
堆排序思想
堆排序的基本思想是利用heap这种数据结构(可看成一个完全二叉树),使在排序中比较的次数明显减少。
堆排序的时间复杂度为O(n*log(n)), 非稳定排序,原地排序(空间复杂度O(1))。
堆排序的关键在于建堆和调整堆,下面简单介绍一下建堆的过程:
第1趟将索引0至n-1处的全部数据建大顶(或小顶)堆,就可以选出这组数据的最大值(或最小值)。将该堆的根节点与这组数据的最后一个节点交换,就使的这组数据中最大(最小)值排在了最后。
第2趟将索引0至n-2处的全部数据建大顶(或小顶)堆,就可以选出这组数据的最大值(或最小值)。将该堆的根节点与这组数据的倒数第二个节点交换,就使的这组数据中最大(最小)值排在了倒数第2位。
…
第k趟将索引0至n-k处的全部数据建大顶(或小顶)堆,就可以选出这组数据的最大值(或最小值)。将该堆的根节点与这组数据的倒数第k个节点交换,就使的这组数据中最大(最小)值排在了倒数第k位。
建堆(初始化+调整堆, 时间复杂度为O(n));
拿堆的根节点和最后一个节点交换(siftdown, 时间复杂度为O(n*log n) ).
是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。
计数排序的思想是在给定的一组序列中,先找出该序列中的最大值和最小值,从而确定需要开辟多大的辅助空间,每一个数在对应的辅助空间中都有唯一的下标。
找出序列中最大值和最小值,开辟Max-Min+1的辅助空间
最小的数对应下标为0的位置,遇到一个数就给对应下标处的值+1,。
遍历一遍辅助空间,就可以得到有序的一组序列
基本思想
桶排序的基本思想是假设数据在[min,max]之间均匀分布,其中min、max分别指数据中的最小值和最大值。那么将区间[min,max]等分成n份,这n个区间便称为n个桶。将数据加入对应的桶中,然后每个桶内单独排序。由于桶之间有大小关系,因此可以从大到小(或从小到大)将桶中元素放入到数组中。
算法思想
基数排序是一种非比较型整数排序算法,其原理是将众多数字按位分隔后进行排序。
实现步骤:
1.将所有待比较的数字(正整数)统一为同一长度,位数不够的数字前面补0;
2.按照从个位,十位,百位······从低到高的顺序进行排序
3.完成从低位到高位的排序后,待排序数字也就完成了排序
- #include <iostream>
- #include <vector>
- #include <algorithm>
- #include <list>
- using namespace std;
-
- vector<int> bubbleSort(vector<int>& nums) {
- int size = nums.size();
- bool flag = false;
- for (int i = 0; i < size; ++i) {
- flag = false;
- for (int j = 0; j < size - i; ++j) {
- if (nums[j] > nums[j + 1]) {
- swap(nums[j], nums[j + 1]);
- flag = true;
- }
- }
- if (!flag) break;
- }
- return nums;
- }
-
-
- vector<int> selectSort(vector<int>& nums) {
- int size = nums.size();
- int index = 0;
- for (int i = 0; i < size; ++i) {
- index = i;
- for (int j = i + 1; j < size; ++j) {
- if (nums[j] < nums[index]) {
- index = j;
- }
- }
- swap(nums[i], nums[index]);
- }
- return nums;
- }
-
-
- vector<int> insertSort(vector<int>& nums) {
- int size = nums.size();
- for (int i = 0; i < size; ++i) {
- int temp = nums[i];
- int j = i;
- while (j > 0 && nums[j - 1] > temp) {
- nums[j] = nums[j - 1];
- j--;
- }
- nums[j] = temp;
- }
- return nums;
- }
-
-
- //quickSort with midValue
- int midValue(vector<int>& nums, int left, int right) {
- int mid = left + (right - left) / 2;
- if (nums[left] < nums[right]) {
- if (nums[mid] > nums[right]) return right;
- else if (nums[mid] < nums[left]) return left;
- else return mid;
- }
- else {
- if (nums[mid] > nums[left]) return left;
- else if (nums[right] > nums[mid]) return right;
- else return mid;
- }
- }
-
-
- vector<int> quickSort(vector<int>& nums, int low, int high) {
- if (low >= high) return nums; //递归一定要有结束标志
- int mid = midValue(nums, low, high);
- swap(nums[low], nums[mid]);
- int first = low, last = high;
- int key = nums[first];
- while (first < last) {
- while (first < last && nums[last] >= key) last--;
- if (first < last) nums[first++] = nums[last];
- while (first < last && nums[first] <= key) first++;
- if (first < last) nums[last--] = nums[first];
- }
- nums[first] = key;
- quickSort(nums, low, first - 1);
- quickSort(nums, first + 1, high);
- return nums;
- }
-
-
-
- vector<int> adjust(vector<int>& nums, int start, int end) {
- int dad = start;
- int son = dad* 2 + 1;
- while (son <= end) {
- if (son + 1 <= end && nums[son] < nums[son + 1])
- son++;
- if (nums[dad] >= nums[son]) break;
- else {
- swap(nums[dad], nums[son]);
- dad = son;
- son = dad* 2 + 1;
- }
- }
- return nums;
- }
-
- vector<int> heapSort(vector<int>& nums, int size) {
- for (int i = size / 2 - 1; i >= 0; --i) {
- adjust(nums, i, size);
- }
- for (int i = size - 1; i >= 0; --i) {
- swap(nums[i], nums[0]);
- adjust(nums, 0, i - 1);
- }
- return nums;
- }
-
-
- vector<int> mergeSort(vector<int>& nums, vector<int>& temp, int start, int end) {
- if (start >= end) return temp;
- int low1 = start, high2 = end, mid = start + (end - start) / 2;
- int high1 = mid, low2 = mid + 1;
- mergeSort( temp, nums,low1, high1);
- mergeSort( temp, nums,low2, high2);
- int index = start; // 缓存数组下标
- while (low1 <= high1 && low2 <= high2) {
- temp[index++] = nums[low1] <= nums[low2] ? nums[low1++] : nums[low2++];
- }
- while (low1 <= high1) temp[index++] = nums[low1++];
- while (low2 <= high2) temp[index++] = nums[low2++];
- // for (index = start; index <= end; ++index) {
- // nums[index] = temp[index];
- // }
- // for (auto a : nums)
- // cout << a << " ";
- // cout << endl;
- return temp;
- }
-
-
-
-
-
- vector<int> shellSort(vector<int>& nums) {
- int size = nums.size();
- for (int gap = size / 2; gap > 0; gap /= 2) { // 注意gap > 0
- for (int i = gap; i < size; ++i) {
- int temp = nums[i];
- int j = i - gap;
- for (; j >= 0 && temp <= nums[j]; j -= gap) { // 注意j >= 0
- nums[j + gap] = nums[j];
- }
- nums[j + gap] = temp;
- }
- }
- return nums;
- }
-
-
- vector<int> countSort(vector<int>& nums, int maximal) { //计数排序要求元素能够作为数组的下标,自然不能是负数
- int len = nums.size();
- if (len < 1) return nums;
- vector<int> count(maximal + 1, 0);
- vector<int> temp(nums);
- for (auto a : nums) {
- count[a]++;
- }
- for (int i = 1; i <= maximal; ++i) {
- count[i] += count[i - 1];
- }
- for (int i = len - 1; i >= 0; --i) {
- nums[count[temp[i]] - 1] = temp[i];
- count[temp[i]]--;
- }
- return nums;
- }
-
-
-
- vector<int> bucketSort(vector<int>& nums, int len) {
- // find the maximal value of array
- int max = nums[0];
- int min = max;
- for (int i = 0; i < len; ++i) {
- if (nums[i] > max) max = nums[i];
- }
- for (int i = 0; i < len; ++i) {
- if (nums[i] < min) min = nums[i];
- }
-
- // get the count of bucket
- int bucketCounts = (max - min) / len + 1;
- vector<vector<int>> bucketArray(bucketCounts);
-
- // assign each value to bucket arrays.
-
- for (int i = 0; i < len; ++i) {
- int num = (nums[i] - min) / len;
- bucketArray[num].push_back(nums[i]);
- }
-
- // sort each bucket
- for (int i = 0; i < bucketCounts; ++i) {
- sort(bucketArray[i].begin(), bucketArray[i].end());
- }
- // collect value from radix arrays to data
- int index = 0;
- for (int i = 0; i < bucketCounts; ++i) {
- for (int j = 0; j < bucketArray[i].size(); ++j) {
- nums[index++] = bucketArray[i][j];
- }
- }
- return nums;
- }
- int my_max(vector<int>& nums, int n){
- int temp = 1;
- int d = 10;
- for (int i = 0; i < n; i++){
- if (nums[i]>d){
- d *= 10;
- temp++;
- }
- }
- return temp;
- }
- vector<int> radixSort(vector<int>& nums, int n){
- int max = my_max(nums, n);
- list<int>my_list[10];
- //最高位决定循环的次数!
- for (int i = 0,d=1; i < max; i++,d*=10){
- for (int j = 0; j < n; j++){
- //核心算法以及注意是按照顺序从前往后插入的!
- my_list[(nums[j] / d) % 10].push_back(nums[j]);
- }
- for (int k = 0,m=0; k < 10; k++){
- while (!my_list[k].empty()){
- //这里我们取出来的时候也是从前往后取出来,这样才是排好序的,同时不要忘记把桶清空呀!
- nums[m++] = my_list[k].front();
- my_list[k].pop_front();
- }
- }
- }
- return nums;
- }
-
-
- int main()
- {
- vector<int> nums{2,4,3,6,5,9,7,1, 11, 16, 13};
- vector<int> temp(nums);
- int maximal = nums[0];
- for (int i = 0; i < nums.size(); ++i) {
- if (nums[i] > maximal) maximal = nums[i];
- }
- int len = nums.size();
- vector<int> res;
- //res = bubbleSort(nums);
- //res = selectSort(nums);
- //res = insertSort(nums);
- //res = quickSort(nums, 0, nums.size() - 1);
- //res = heapSort(nums,nums.size());
- //res = mergeSort(nums, temp, 0, nums.size() - 1);
- //res = shellSort(nums);
- //res = countSort(nums, maximal);
- //res = bucketSort(nums, len);
- res = radixSort(nums, len);
- for (auto a : res)
- cout << a << " ";
-
- }
注意:十个排序算法都已经测试过,其中需要注意的是由于计数排序需要元素作为下标,所有我给测试数据时候,都用的是正数。
优化:比如冒泡排序,我们可以采用flag判断如果已经有序,我们可以直接中断循环,节省时间;
比如快速排序,我们采用三个数每次都取最中间的数,这样的目的是让快排结果不会有最坏结果。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。