八大排序算法总结(1)
首先,排序算法的稳定性大家应该都知道,通俗地讲就是能保证排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同。在简单形式化一下,如果Ai = Aj, Ai原来在位置前,排序后Ai还是要在Aj位置前。
其次,说一下稳定性的好处。排序算法如果是稳定的,那么从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用。基数排序就 是这样,先按低位排序,逐次按高位排序,低位相同的元素其顺序再高位也相同时是不会改变的。
回到主题,现在分析一下常见的排序算法的稳定性,每个都给出简单的理由。
(1)冒泡排序
冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,我想你是不会再无聊地把他们俩交换一下的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改 变,所以冒泡排序是一种稳定排序算法。
(2)选择排序
选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个 元素不用选择了,因为只剩下它一个最大的元素了。那么,在一趟选择,如果当前元素比一个元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么 交换后稳定性就被破坏了。比较拗口,举个例子,序列5 8 5 2 9, 我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。
(3)插入排序
插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。当然,刚开始这个有序的小序列只有1个元素,就是第一个元素。比较是从有序序列的末尾开 始,也就是想要插入的元素和已经有序的最大者开始比起,如果比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。如果碰见一个和插入元素相 等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳 定的。
(4)快速排序
快速排序有两个方向,左边的i下标一直往右走,当a[i] <= a[center_index],其中center_index是中枢元素的数组下标,一般取为数组第0个元素。而右边的j下标一直往左走,当a[j] > a[center_index]。如果i和j都走不动了,i <= j, 交换a[i]和a[j],重复上面的过程,直到i>j。 交换a[j]和a[center_index],完成一趟快速排序。在中枢元素和a[j]交换的时候,很有可能把前面的元素的稳定性打乱,比如序列为 5 3 3 4 3 8 9 10 11, 现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱,所以快速排序是一个不稳定的排序算法,不稳定发生在中枢元素和a[j] 交换的时刻。
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <vector> using namespace std; void swap(vector<int>& nums, int i, int j) { int t = nums[i]; nums[i] = nums[j]; nums[j] = t; }; void bubbleSort(vector<int>& nums) { for(int i = nums.size() - 1; i>=0 ;--i) { for (int j = 0; j < i; ++j) { if(nums[j]>nums[j+1]) { swap(nums,j,j+1); } } } }; void selectionSort(vector<int>& nums) { for(int i = 0; i < nums.size(); ++i) { int min_index = i; for(int j = i+1;j < nums.size();++j) { if (nums[j] <= nums[min_index]) { min_index = j; } } swap(nums,min_index,i); } } void insertSort(vector<int>& nums) { for(int i = 1;i < nums.size(); ++i) { int v = nums[i]; int p = i; while(p>0 && v < nums[p-1]) { nums[p] = nums[p-1]; p--; } nums[p] = v; } } int main() { vector<int> nums = {1,4,5,2,6,3}; //bubbleSort(nums); //selectionSort(nums); insertSort(nums); for (auto i : nums) { cout << ' ' << i << ' ' ; } return 0; }
冒泡排序:
第一轮:从下标0到n-1(n 是数组长度),如果前一个元素比后一个元素大,那么,相邻的两两交换,最后数组中最大的元素放在最后一个位置上。
第二轮:从下标0到n-2,重复上过程,这样第二大的元素,就放在了倒数第二个位置上。
。。。。。。。
冒泡排序的基本思想是,对相邻的元素进行两两比较,顺序相反则进行交换,这样,每一趟会将最小或最大的元素“浮”到顶端,最终达到完全有序
//冒泡排序 public static void buddleSort(int[] a ){ for(int i =0;i<a.length-1;i++) for(int j=0;j<a.length-1-i;j++) if(a[j]>a[j+1]) swap(a, j+1, j); }
1 def bubble_sort(x): 2 ''' 3 bubble sort 4 每次将最大的元素放在最后一位 5 循环N次 6 7 8 9 10 [6, 5, 1, 4] 11 [5, 6, 1, 4] 12 [5, 1, 6, 4] 13 [5, 1, 4, 6] 14 15 [1, 5, 4, 6] 16 [1, 4, 5, 6] 17 18 [1, 4, 5, 6] 19 Arguments: 20 x {[type]} -- [description] 21 22 Returns: 23 [type] -- [description] 24 ''' 25 for i in range(len(x)): 26 for j in range(len(x) - 1 - i): 27 #for j in range(len(x)-1-i-1,-1,-1):28 if(x[j] > x[j + 1]): 29 swap(x, j, j + 1) 30 return x
选择排序:
第一轮:从下标0到n-1,找出最小元素的下标min, 交换min 与 0,这样第一个位置放的是最小的元素。
第二轮:从下标1到n-1,找出最小元素的下标min,交换min与1,这样第二个位置放的是第二小的元素。
。。。。。。。
//选择排序 public static void choseSort(int[] a ) { int min; for(int i=0;i<=a.length-1;i++){ min = i; for(int j=i;j<=a.length-1;j++) if(a[j]<a[min]) min=j; if(min!=i) swap(a, min, i); } }
1 def chouse_sort(x): 2 '''[summary] 3 4 选择一个最小的元素放在前面 5 6 7 Arguments: 8 x {[type]} -- [description] 9 ''' 10 11 for i in range(len(x)): 12 minx = i 13 for j in range(i, len(x)): 14 if(x[j] < x[minx]): 15 minx = j 16 swap(x, i, minx) 17 return x
插入排序:
直接插入排序基本思想是每一步将一个待排序的记录,插入到前面已经排好序的有序序列中去,直到插完所有元素为止。
辅助函数: