排序算法(Java)
来源:互联网 发布:软件测试工作怎么样 编辑:程序博客网 时间:2024/05/23 18:51
控制台运行编译java程序带中文乱码问题解决办法: javac -encoding utf-8 Test.java
稳定性:排序算法需要保留数组中重复元素的相对位置。(具体详见算法第四版P217)
冒泡排序
思想: 两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。
时间复杂度: O(N2)
空间复杂度: O(1)
稳定性: 稳定
public class Sort { public static void sort(int[] nums) { int N = nums.length; int temp = 0; for (int i = 0; i < N-1; i++) { for (int j = 0; j < N-i-1; j++) { if (nums[j] > nums[j + 1]) { temp = nums[j]; nums[j] = nums[j+1]; nums[j+1] = temp; } } } }}
选择排序
思想: 每一趟在n-i+1(i=1,2,…,n-1)个记录中选取关键字最小的记录作为有序序列的第i个记录。
时间复杂度: O(N2)
空间复杂度: O(1)
稳定性: 不稳定(说明:举个例子,序列5 8 5 2 9, 我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法)
public class Sort { public static void sort(int[] nums) { int N = nums.length; for (int i = 0; i < N; i++ ) { int min = i; for (int j = i+1; j < N; j++) { if (nums[j] < nums[min]) { min = j; } } if (min != i) { temp = nums[i]; nums[i] = nums[min]; nums[min] = temp; } } }}
插入排序
思想: 将一个记录插入到已经排序好的有序表中,从而得到一个新的、记录数增1的有序表
时间复杂度: O(N2)
空间复杂度: O(1)
稳定性: 稳定
public class Sort { public static void sort(int[] nums) { int N = nums.length; int temp = 0; for (int i = 1; i < N; i++ ) { for (int j = i; j > 0 && nums[j] < nums[j-1]; j--) { temp = nums[j]; nums[j] = nums[j+1]; nums[j+1] = temp; } } }}
改进版本
public class Sort { public static void sort(int[] nums) { int N = nums.length; int i = 0; int j = 0; for (i = 1; i < N; i++) { if (nums[i] < nums[i-1]) { int temp = nums[i]; for (j = i-1; (j >= 0) && (nums[j] > temp); j--) { nums[j+1] = nums[j]; } nums[j+1] = temp; } } }}
希尔排序(插入排序升级)
思想: 将数据分为若干组记录,然后分别对每一组做插入排序。
时间复杂度: O(NlgN)
空间复杂度: O(1)
稳定性: 不稳定
public class Sort { public static void sort(int[] nums) { int N = nums.length; int temp = 0; int h = 1; int i = 0; int j = 0; //固定步长:1,4,13,40... while (h < N/3) { h = 3*h + 1; } while (h >= 1) { //下面操作和插入排序算法基本相同 for (i = h; i < N; i++) { if (nums[i] < nums[i - h]) { int temp = nums[i]; for (j = i-h; (j >= 0) && (nums[j] > temp); j = j-h) { nums[j+h] = nums[j]; } nums[j+h] = temp; } } h = h / 3; } }}
快速排序(冒泡排序增强)
思想: 选取一个轴值(比较的基准),将待排序记录分为独立的两个部分,左侧记录都是小于或等于轴值,右侧记录都是大于或等于轴值,然后分别对左侧部分和右侧部分重复前面的过程,也就是左侧部分又选择一个轴值,又分为两个独立的部分,这就使用了递归了。到最后,整个序列就变得有序了。
时间复杂度: O(NlgN)
空间复杂度: O(lgN)~O(N)
稳定性: 不稳定
public class Sort { public static void sort(int[] nums) { //消除对输入的依赖 //StdRandom.shuffle(a); sort(nums, 0, nums.length - 1); } public static void sort (int[] nums, int lo, int hi) { if (hi <= lo) return; //切分 int j = partition(nums, lo, hi); sort(nums, lo, j-1); sort(nums, j+1, hi); } public static int partition(int[] nums, int lo, int hi) { //定义左右扫描的指针 int i = lo, j = hi + 1; //定义切分的元素d int point = nums[lo]; int temp = 0; while (true) { while (nums[++i] < point) if (i == hi) break; while (point < nums[--j]) if (j == lo) break; if (i >= j) break; //交换两个元素 temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; } //将切分元素放到数组中合适位置 temp = nums[lo]; nums[lo] = nums[j]; nums[j] = temp; return j; }}
堆排序(选择排序增强)
思想: 利用优先队列的删除最大元素的特点,依次将将删除的最大元素保存起来,就有序了。当然,这里的优先队列是用堆实现的。
第一步:需要保证堆有序,也就是每一个父节点要比它的任意子节点要大;
第二步:使用堆的下沉操作来实现排序;
时间复杂度: O(NlgN)
空间复杂度: O(1)
稳定性: 不稳定
public class Sort { public static void heapSort(int[] a) { int N = a.length; //第一步:通过下沉操作来实现堆有序,这里只需要操作数组中前面的一半元素即可 for (int k = N / 2; k >= 1; k--) { sink(a, k, N); } //第二步:将堆有序的数组使用下沉操作来得到有序数组 while (N > 1) { exch(a, 1, N--); sink(a, 1, N); } } //堆下沉操作 public static void sink(int[] a, int k, int N) { while (2*k <= N) { int j = 2*k; //取两个子节点中较大的一个 if (j < N && less(a, j, j+1)) j++; //比较如果父节点比子节点中较大的一个小,则交换 if (!less(a, k, j)) break; exch(a, k, j); //继续往下面遍历 k = j; } } //之所以取i-1,是因为堆中下标是从1开始的,需要还原到数组中从0开始的。 public static boolean less(int[] a, int i, int j) { return a[i-1] < a[j-1]; } public static void exch(int[] a, int i, int j) { int temp = a[i-1]; a[i-1] = a[j-1]; a[j-1] = temp; } }
归并排序
思想: 要将一个数组排序,可以先(递归地)将它们分成两半分别排序,然后将它们的结果归并起来。
时间复杂度: O(NlgN)
空间复杂度: O(N)
稳定性: 稳定
public class Sort { //1.将两个有序数组合并为一个有序数组 public static void merge(int[] a, int lo, int mid, int hi) { //将a[lo..mid] 和 a[mid+1..hi]归并 //i代表左半边索引,j代表右半边索引 int i = lo, j = mid + 1; //定义一个辅助数组 int[] aux; //将a[lo..hi]复制到aux[lo..hi] for (int k = lo; k <= hi; k++) { aux[k] = a[k]; } for (int k = lo; k <= hi; k++) { //左半边元素用尽,取右半边的元素 if (i > mid) { a[k] = aux[j++]; //右半边元素用尽,取左半边元素 } else if (j > hi) { a[k] = aux[i++]; //右半边的当前元素小于左半边的当前元素,则取右半边的元素 } else if (aux[j] < aux[i]) { a[k] = aux[j++]; //左半边的当前元素小于等于右半边的当前元素,则取左半边的元素 } else { a[k] = aux[i++]; } } } //自顶向下 pubic static void sort (int[] a, int lo, int hi) { if (hi <= lo) { return; } int mid = lo + (hi - lo) / 2; //将左半边排序 sort(a, lo, mid); //将右半部分排序 sort(a, mid+1, hi); //归并结果 merger(a, lo, mid, hi); } //自底向上 public static void sort (int[] a) { int N = a.length; int[] aux = new int[N]; //定义子数组的大小 for (int sz = 1; sz < N; sz = sz + sz) { //子数组的索引 for (int lo = 0; lo < N-sz; lo += sz+sz) { //lo+sz+sz-1可能越界,因此需要一个min函数来取边界 merge(a, lo, lo+sz-1, Math.min(lo+sz+sz-1, N-1)); } } }}
总结
时间复杂度:
O(N)
计数排序、基数排序
O(N2)
冒泡排序、选择排序、插入排序
O(NlogN)
希尔排序、堆排序、快速排序、归并排序
空间复杂度:
O(1)
插入排序、选择排序、冒泡排序、堆排序、希尔排序
O(logN) ~ O(N)
快速排序
O(N)
归并排序
O(M) M为桶的数量
计数排序、基数排序
稳定性:
稳定的排序算法:
冒泡排序、插入排序、归并排序、计数排序、基数排序、桶排序
不稳定的排序算法:
选择排序、快速排序、希尔排序、堆排序
- 排序算法(java)
- 排序算法(java)
- 排序算法(Java)
- Java排序算法(三):堆排序
- Java排序算法(四):冒泡排序
- Java排序算法(五):快速排序
- Java排序算法(九):归并排序
- Java排序算法(三):堆排序
- Java排序算法(四):冒泡排序
- Java排序算法(五):快速排序
- 老生常谈 排序算法(JAVA)-- 归并排序
- #算法排序(Java版)#快速排序
- Java排序算法(三):堆排序
- Java排序算法(四):冒泡排序
- Java排序算法(五):快速排序
- Java排序算法(九):归并排序
- Java排序算法(九):归并排序
- Java排序算法(三):堆排序 .
- SVM(support vector machine)支持向量机原理详解
- wpf 自定义控件的使用
- 关于Android Studio配置Gradle
- [转]PWM详解
- Oracle中查看无效的对象、约束、触发器和索引
- 排序算法(Java)
- Global.asax的作用
- Jenkins之参数化构建过程
- python技巧
- SpringData JPA 实现动态条件查询
- MySQL:浅析 Impossible WHERE noticed after reading const tables
- 位运算基础知识
- Concurrent Set的创建
- D12 Hive基础及Flume