数据结构排序算法的概念是从网上抄录的:
基本概念:
1、 排序:按照一定的关键字,将一个序列排列成想要得到的一个新的序列。
2、 内部排序和外部排序:整个排序过程完全在内存中进行,叫做内部排序。数据量较大需要借助外部存储设备才能完成,叫做外部排序。
3、 主关键字和此关键字:
4、 排序的稳定性:对于相同的元素来说,在排序之前和之后的书讯是一样的,那么这种排序就是稳定的排序,如果顺序发生了变化,那么就是不稳定排序。
插入类排序:
(一) 思想:在一个已经排好序的序列中,将未被排进的元素按照原先的规定插入到指定位置。
(二) 分类:
1、 直接插入排序:
① 思想:最基本的插入排序,将第i个插入到前i-1个中的适当位置。
② 时间复杂度:T(n) = O(n)。
③ 空间复杂度:S(n) = O(1)。
④ 稳定性:稳定排序。循环条件while(r[0].key < r[j].key)保证的。
⑤ 程序:
/** * 直接插入排序: * 1.从第一个元素开始,该元素可以认为已经被排序 * 2.取出下一个元素,在已经排序的元素序列中从后向前扫描 * 3.如果该元素小于前面的元素(已排序),则依次与前面元素进行比较如果小于则交换,直到找到大于该元素的就则停止; * 4.如果该元素大于前面的元素(已排序),则重复步骤2 5.重复步骤2~4 直到所有元素都排好序 。 * * @param a */ public static void insert(int[] a) { for (int i = 1; i < a.length; i++) { int key = a[i]; for (int j = i - 1; j >= 0; j--) { if (key < a[j]) { int temp = a[j]; a[j] = key; a[i] = temp; i = j; } } } }
2、 折半插入排序:
① 思想:因为是已经确定了前部分是有序序列,所以在查找插入位置的时候可以用折半查找的方法进行查找,提高效率。
② 时间复杂度:比较时的时间减为O(n�Sn),但是移动元素的时间耗费未变,所以总是得时间复杂度还是O(n)。
③ 空间复杂度:S(n) = O(1)。
④ 稳定性:稳定排序。
⑤ 程序:
/** * 折半插入排序: * 在将一个新元素插入已排好序的数组的过程中,寻找插入点时, 将待插入区域的首元素设置为a[low],末元素设置为a[high], * 比较时将待插入元素与a[m],其中m=(low+high)/2相比较,如果比参考元素大, * 则选择a[low]到a[m-1]为新的插入区域(即high=m-1), 否则选择a[m+1]到a[high]为新的插入区域(即low=m+1), * 如此直至low<=high不成立,即将此位置之后所有元素后移一位, 并将新元素插入a[high+1]。 * * @param a */ public static void binaryInsert(int[] a) { // {1, 5, 5, 3, 4, 3,2} for (int i = 1; i < a.length; i++) { int key = a[i]; int low = 0; int high = i - 1; while (low <= high) { int mid = (low + high) / 2; if (key < a[mid]) { high = mid - 1; } else { low = mid + 1; } } System.out.println("low:" + low + ",high:" + high + ",i:" + i + ",key:" + key); for (int j = i; j > high + 1; j--) { a[j] = a[j - 1]; } a[high + 1] = key; } }
3、shell排序
交换类排序:
(一) 思想:通过交换逆序元素进行排序的方法。
(二) 分类:
1、 冒泡排序:
① 思想:反复扫描待排序序列,在扫描的过程中顺次比较相邻的两个元素的大小,若逆序就交换位置。第一趟,从第一个数据开始,比较相邻的两个数据,(以升序为例)如果大就交换,得到一个最大数据在末尾;然后进行第二趟,只扫描前n-1个元素,得到次大的放在倒数第二位。以此类推,最后得到升序序列。如果在扫描过程中,发现没有交换,说明已经排好序列,直接终止扫描。所以最多进行n-1趟扫描。
② 时间复杂度:T(n) = O(n)。
③ 空间复杂度:S(n) = O(1)。
④ 稳定性:稳定排序。
⑤ 程序:
/** * 冒泡排序: * 依次比较相邻的两个数,将小数放在前面,大数放在后面。 即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。 * 然后比较第2个数和第3个数,将小数放前,大数放后,如此继续, 直至比较最后两个数,将小数放前,大数放后。 至此第一趟结束,将最大的数放到了最后。 * 依次执行以上步骤a.leng-1次,排序完成。 * * @param a */ public static void bubble(int[] a) { // {1, 5, 5, 3, 4, 3,2} for (int i = 1; i < a.length; i++) { for (int j = 0; j < a.length - i; j++) { if (a[j + 1] < a[j]) { int temp = a[j + 1]; a[j + 1] = a[j]; a[j] = temp; } } } }
2、 快速排序:
① 思想:冒泡排序一次只能消除一个逆序,为了能一次消除多个逆序,采用快速排序。以一个关键字为轴,从左从右依次与其进行对比,然后交换,第一趟结束后,可以把序列分为两个子序列,然后再分段进行快速排序,达到高效。
② 时间复杂度:平均T(n) = O(n�Sn),最坏O(n)。
③ 空间复杂度:S(n) = O(�Sn)。
④ 稳定性:不稳定排序。{3, 2, 2}
⑤ 程序:
/** * 快速排序: * 一趟快速排序的算法是: * 1)设置两个变量l、h,排序开始的时候:l=0,h=N-1; * 2)以第一个数组元素作为关键数据,赋值给key,即key=A[0]; * 3)从j开始向前搜索,即由后开始向前搜索,找到第一个小于key的值A[j],将A[j]和A[i]互换,然后h--; * 4)从i开始向后搜索,即由前开始向后搜索,找到第一个大于key的A[i],将A[i]和A[j]互换,然后l++; * 5)重复第3、4步,直到l==h * * @param a */ public static void quick(int[] a, int l, int h) { int low = l; int high = h; int key = a[low]; while (low < high) { //从后往前找 while (low < high && key < a[high]) { high--; } if (low < high) { int temp = a[high]; a[high] = a[low]; a[low] = temp; low++; } //从前往后找 while (low < high && key > a[low]) { low++; } if (low < high) { int temp = a[high]; a[high] = a[low]; a[low] = temp; high--; } } if (low > l) { quick(a, l, low-1); } if (high < h) { quick(a, high+1, h); } }
选择类排序:
(一) 思想:每一趟在n �C i + 1 ( i = 1,2, … , n - 1)个记录中选取关键字最小的记录作为有序序列中的第i个记录。
(二) 分类:
1、 简单选择排序:
① 思想:第一趟时,从第一个记录开始,通过n �C 1次关键字的比较,从n个记录中选出关键字最小的记录,并和第一个记录进行交换。第二趟从第二个记录开始,选择最小的和第二个记录交换。以此类推,直至全部排序完毕。
② 时间复杂度:T(n) = O(n)。
③ 空间复杂度:S(n) = O(1)。
④ 稳定性:不稳定排序,{3, 3, 2}。
⑤ 程序:
/** * 选择排序: * 在第一趟遍历N个数据,找出其中最小的数值与第一个元素交换, * 第二趟遍历剩下的N-1个数据,找出其中最小的数值与第二个元素交换...... * 第N-1趟遍历剩下的2个数据,找出其中最小的数值与第N-1个元素交换, * 至此选择排序完成。 * @param a */ public static void selectSort(int[] a) { //{ 1, 5, 5, 3, 4, 3, 2 }; for(int i=1; i<a.length; i++) { int flag = i-1; for(int j=i; j<a.length; j++) { if(a[flag] > a[j]) { flag = j; } } if(flag != i-1) { int temp = a[flag]; a[flag] = a[i-1]; a[i-1] = temp; } toString(a); } }
3、 堆排序:
① 思想:把待排序记录的关键字存放在数组r[1…n]中,将r看成是一刻完全二叉树的顺序表示,每个节点表示一个记录,第一个记录r[1]作为二叉树的根,一下个记录r[2…n]依次逐层从左到右顺序排列,任意节点r[i]的左孩子是r[2i],右孩子是r[2i+1],双亲是r[i/2向下取整]。然后对这棵完全二叉树进行调整建堆。
② 时间复杂度:T(n) = O(n�Sn)。
③ 空间复杂度:S(n) = O(1)。
④ 稳定性:不稳定排序。{5, 5, 3}