首页 > 编程 > Java > 正文

java 排序算法

2019-11-06 09:33:32
字体:
来源:转载
供稿:网友

排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。我们这里说说八大排序就是内部排序。 日常操作中常见的排序方法有:插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序、基数排序等。

一、插入排序

1 原理

插入排序的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的字序列的合适位置(从后向前找到合适位置后),直到全部插入排序完为止。

2 实现步骤

经常碰到这样一类排序问题:把新的数据插入到已经排好的数据列中。 (1)将第一个数和第二个数排序,然后构成一个有序序列 (2)将第三个数插入进去,构成一个新的有序序列。 (3)对第四个数、第五个数……直到最后一个数,重复第二步。 这里写图片描述

3 实现逻辑

(1)首先设定插入次数,即循环次数,for(int i=1;i<length;i++),1个数的那次不用插入。 (2)设定插入数和得到已经排好序列的最后一个数的位数。insertNum和j=i-1。 (3)从最后一个数开始向前循环,如果插入数小于当前数,就将当前数向后移动一位。 (4)将当前数放置到空着的位置,即j+1。

4 代码实现

public void insertSort(int[] numbers){ int size = numbers.length;//数组长度,将这个提取出来是为了提高速度。 int insertNum;//要插入的数 for(int i = 1;i<size ;i++){//插入的次数 insertNum=numbers[i];//要插入的数 int j=i-1;//已经排序好的序列元素个数 //序列从后到前循环,将大于insertNum的数向后移动一格 while(j>=0&&numbers[j]>insertNum){ numbers[j+1]=numbers[j];//元素移动一格 j--; } numbers[j+1]=insertNum;//将需要插入的数放在要插入的位置。 }}public void insertSort(int[] numbers){ int size = numbers.length;//数组长度,将这个提取出来是为了提高速度。 int insertNum= 0;//要插入的数 int j = 0; for(int i = 0 ; i < size ; i++){ insertNum = numbers[i];//要插入的数 //假如insertNum比前面的值小,则将前面的值后移 for(j = i ; j > 0 && insertNum < numbers[j-1] ; j--){ numbers[j] = numbers[j-1];//元素移动一格 } numbers[j] = insertNum;//将需要插入的数放在要插入的位置。 }}

二、希尔排序

1 原理

希尔排序是1959 年由D.L.Shell 提出来的,相对直接排序有较大的改进。希尔排序又叫缩小增量排序。

先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。

2 实现步骤

对于直接插入排序问题,数据量巨大时。 (1)将数的个数设为n,取奇数k=n/2,将下标差值为k的书分为一组,构成有序序列。 (2)再取k=k/2 ,将下标差值为k的书分为一组,构成有序序列。 (3)重复第二步,直到k=1执行简单插入排序。 这里写图片描述

3 实现逻辑

(1)首先确定分的组数。 (2)然后对组中元素进行插入排序。 (3)然后将length/2,重复1,2步,直到length=0为止。

4 代码实现

public void shellSort(int[] a){ int d = a.length; while (d!=0) { d=d/2; for (int x = 0; x < d; x++) {//分的组数 for (int i = x + d; i < a.length; i += d) {//组中的元素,从第二个数开始 int j = i - d;//j为有序序列最后一位的位数 int temp = a[i];//要插入的元素 for (; j >= 0 && temp < a[j]; j -= d) {//从后往前遍历。 a[j + d] = a[j];//向后移动d位 } a[j + d] = temp; } } }}public static void shellSort(int[] data){ int j = 0; int temp = 0; //每次将步长缩短为原来的一半 for (int increment = data.length / 2; increment > 0; increment /= 2){ for (int i = increment; i < data.length; i++){ temp = data[i]; for (j = i; j >= increment; j -= increment){ //如想从小到大排只需修改这里 if(temp > data[j - increment]){ data[j] = data[j - increment]; }else{ break; } } data[j] = temp; } }}

三、选择排序

1 原理

在要排序的一组数中,选出最小的一个数与第一个位置的数交换;然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。

常用于取序列中最大最小的几个数时。

如果每次比较都交换,那么就是交换排序;如果每次比较完一个循环再交换,就是简单选择排序。

2 实现步骤

(1)遍历整个序列,将最小的数放在最前面。 (2)遍历剩下的序列,将最小的数放在最前面。 (3)重复第二步,直到只剩下一个数。 这里写图片描述

3 实现逻辑

(1)首先确定循环次数,并且记住当前数字和当前位置。 (2)将当前位置后面所有的数与当前数字进行对比,小数赋值给key,并记住小数的位置。 (3)比对完成后,将最小的值与第一个数的值交换。 (4)重复2、3步。

4 代码实现

public void selectSort(int[] a) { int length = a.length; for (int i = 0; i < length; i++) {//循环次数 int key = a[i]; int position=i; for (int j = i + 1; j < length; j++) {//选出最小的值和位置 if (a[j] < key) { key = a[j]; position = j; } } a[position]=a[i];//交换位置 a[i]=key; }}public static void selectSort(int[] numbers){ int size = numbers.length; //数组长度 int temp = 0 ; //中间变量 for(int i = 0 ; i < size ; i++){ int k = i; //待确定的位置 //选择出应该在第i个位置的数 for(int j = size -1 ; j > i ; j--){ if(numbers[j] < numbers[k]){ k = j; } } //交换两个数 temp = numbers[i]; numbers[i] = numbers[k]; numbers[k] = temp; }}

四、堆排序

1 原理

具有n个元素的序列 (h1,h2,…,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,…,n/2)时称之为堆。

完全二叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。

堆排序是一种树形选择排序,是对直接选择排序的有效改进。

2 实现步骤

(1)将序列构建成大顶堆。 (2)将根节点与最后一个节点交换,然后断开最后一个节点。 (3)重复第一、二步,直到所有节点断开。 这里写图片描述

3 实现逻辑

(1)初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个堆,这时堆的根节点的数最大。 (2)将根节点与堆的最后一个节点交换。 (3)对前面(n-1)个数重新调整使之成为堆。 (4)依此类推,直到只有两个节点的堆,并对 它们作交换,最后得到有n个节点的有序序列。 从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。

4 代码实现

public class HeapSort { public static void main(String[] args) { int[] a={49,38,65,97,76,13,27,49,78,34,12,64}; int arrayLength=a.length; //循环建堆 for(int i=0;i<arrayLength-1;i++){ //建堆 buildMaxHeap(a,arrayLength-1-i); //交换堆顶和最后一个元素 swap(a,0,arrayLength-1-i); System.out.PRintln(Arrays.toString(a)); } } //对data数组从0到lastIndex建大顶堆 public static void buildMaxHeap(int[] data, int lastIndex){ //从lastIndex处节点(最后一个节点)的父节点开始 for(int i=(lastIndex-1)/2;i>=0;i--){ //k保存正在判断的节点 int k=i; //如果当前k节点的子节点存在 while(k*2+1<=lastIndex){ //k节点的左子节点的索引 int biggerIndex=2*k+1; //如果biggerIndex小于lastIndex,即biggerIndex+1代表的k节点的右子节点存在 if(biggerIndex<lastIndex){ //若果右子节点的值较大 if(data[biggerIndex]<data[biggerIndex+1]){ //biggerIndex总是记录较大子节点的索引 biggerIndex++; } } //如果k节点的值小于其较大的子节点的值 if(data[k]<data[biggerIndex]){ //交换他们 swap(data,k,biggerIndex); //将biggerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值大于其左右子节点的值 k=biggerIndex; }else{ break; } } } } //交换 private static void swap(int[] data, int i, int j) { int tmp=data[i]; data[i]=data[j]; data[j]=tmp; } }

五、冒泡排序

1 原理

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

2 实现步骤

(1)将序列中所有元素两两比较,将最大的放在最后面。 (2)将剩余序列中所有元素两两比较,将最大的放在最后面。 (3)重复第二步,直到只剩下一个数。 这里写图片描述

3 实现逻辑

(1)设置循环次数。 (2)设置开始比较的位数,和结束的位数。 (3)两两比较,将最小的放到前面去。 (4)重复2、3步,直到循环次数完毕。

4 代码实现

public void bubbleSort(int[] a){ int length=a.length; int temp; for(int i=0;i<a.length;i++){ for(int j=0;j<a.length-i-1;j++){ if(a[j]>a[j+1]){ temp=a[j]; a[j]=a[j+1]; a[j+1]=temp; } } }}

六、快速排序

1 原理

通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分关键字小,则分别对这两部分继续进行排序,直到整个序列有序。

2 实现步骤

(1)选择第一个数为p,小于p的数放在左边,大于p的数放在右边。 (2)递归的将p左边和右边的数都按照第一步进行,直到不能递归。 这里写图片描述

3 实现逻辑

(1)把整个序列看做一个数组,把第零个位置看做中轴,和最后一个比,如果比它小交换,比它大不做任何处理。 (2)交换了以后再和小的那端比,比它小不交换,比他大交换。这样循环往复,一趟排序完成,左边就是比中轴小的,右边就是比中轴大的,然后再用分治法,分别对这两个独立的数组进行排序。

4 代码实现

public static void quickSort(int[] numbers, int start, int end) { if (start < end) { int base = numbers[start]; // 选定的基准值(第一个数值作为基准值) int temp; // 记录临时中间值 int i = start, j = end; do { while ((numbers[i] < base) && (i < end)) i++; while ((numbers[j] > base) && (j > start)) j--; if (i <= j) { temp = numbers[i]; numbers[i] = numbers[j]; numbers[j] = temp; i++; j--; } } while (i <= j); if (start < j) quickSort(numbers, start, j); if (end > i) quickSort(numbers, i, end); } }

快速排序是通常被认为在同数量级(O(nlog2n))的排序方法中平均性能最好的。但若初始序列按关键码有序或基本有序时,快排序反而蜕化为冒泡排序。为改进之,通常以“三者取中法”来选取基准记录,即将排序区间的两个端点与中点三个记录关键码居中的调整为支点记录。快速排序是一个不稳定的排序方法。

七、归并排序

1 原理

归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

速度仅次于快排,内存少的时候使用,可以进行并行计算的时候使用。

2 实现步骤

(1)选择相邻两个数组成一个有序序列。 (2)选择相邻的两个有序序列组成一个有序序列。 (3)重复第二步,直到全部组成一个有序序列。 这里写图片描述

3 实现逻辑

设r[i…n]由两个有序子表r[i…m]和r[m+1…n]组成,两个子表长度分别为n-i +1、n-m。 (1)j=m+1;k=i;i=i; //置两个子表的起始下标及辅助数组的起始下标 (2)若i>m 或j>n,转⑷ //其中一个子表已合并完,比较选取结束 (3)//选取r[i]和r[j]较小的存入辅助数组rf 如果r[i]<r[j],rf[k]=r[i]; i++; k++; 转⑵ 否则,rf[k]=r[j]; j++; k++; 转⑵ (4)//将尚未处理完的子表中元素存入rf 如果i<=m,将r[i…m]存入rf[k…n] //前一子表非空 如果j<=n , 将r[j…n] 存入rf[k…n] //后一子表非空 合并结束。

4 代码实现

public static void mergeSort(int[] numbers, int left, int right) { int t = 1;// 每组元素个数 int size = right - left + 1; while (t < size) { int s = t;// 本次循环每组元素个数 t = 2 * s; int i = left; while (i + (t - 1) < size) { merge(numbers, i, i + (s - 1), i + (t - 1)); i += t; } if (i + (s - 1) < right) merge(numbers, i, i + (s - 1), right); } } private static void merge(int[] data, int p, int q, int r) { int[] B = new int[data.length]; int s = p; int t = q + 1; int k = p; while (s <= q && t <= r) { if (data[s] <= data[t]) { B[k] = data[s]; s++; } else { B[k] = data[t]; t++; } k++; } if (s == q + 1) B[k++] = data[t++]; else B[k++] = data[s++]; for (int i = p; i <= r; i++) data[i] = B[i]; }/** * 归并排序 * 简介:将两个(或两个以上)有序表合并成一个新的有序表 即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列 * 时间复杂度为O(nlogn) * 稳定排序方式 * @param nums 待排序数组 * @return 输出有序数组 */public static int[] sort(int[] nums, int low, int high) { int mid = (low + high) / 2; if (low < high) { // 左边 sort(nums, low, mid); // 右边 sort(nums, mid + 1, high); // 左右归并 merge(nums, low, mid, high); } return nums;}/** * 将数组中low到high位置的数进行排序 * @param nums 待排序数组 * @param low 待排的开始位置 * @param mid 待排中间位置 * @param high 待排结束位置 */public static void merge(int[] nums, int low, int mid, int high) { int[] temp = new int[high - low + 1]; int i = low;// 左指针 int j = mid + 1;// 右指针 int k = 0; // 把较小的数先移到新数组中 while (i <= mid && j <= high) { if (nums[i] < nums[j]) { temp[k++] = nums[i++]; } else { temp[k++] = nums[j++]; } } // 把左边剩余的数移入数组 while (i <= mid) { temp[k++] = nums[i++]; } // 把右边边剩余的数移入数组 while (j <= high) { temp[k++] = nums[j++]; } // 把新数组中的数覆盖nums数组 for (int k2 = 0; k2 < temp.length; k2++) { nums[k2 + low] = temp[k2]; }}

八、基数排序

1 原理

基数排序已经不再是一种常规的排序方式,它更多地像一种排序方法的应用,基数排序必须依赖于另外的排序方法。基数排序的总体思路就是将待排序数据拆分成多个关键字进行排序,也就是说,基数排序的实质是多关键字排序。

多关键字排序的思路是将待排数据里德排序关键字拆分成多个排序关键字;第1个排序关键字,第2个排序关键字,第3个排序关键字……然后,根据子关键字对待排序数据进行排序。

用于大量数,很长的数进行排序时。

2 实现步骤

(1)将所有的数的个位数取出,按照个位数进行排序,构成一个序列。 (2)将新构成的所有的数的十位数取出,按照十位数进行排序,构成一个序列。 这里写图片描述

3 实现逻辑

最高位优先(Most Significant Digit first)法,简称MSD法: (1)先按k1排序分组,同一组中记录,关键码k1相等。 (2)再对各组按k2排序分成子组。 (3)之后,对后面的关键码继续这样的排序分组,直到按最次位关键码kd对各子组排序后。再将各组连接起来,便得到一个有序序列。 最低位优先(Least Significant Digit first)法,简称LSD法: (1)先从kd开始排序。 (2)再对kd-1进行排序。 (3)依次重复,直到对k1排序后便得到一个有序序列。

4 代码实现

public void sort(int[] array) { //首先确定排序的趟数; int max = array[0]; for (int i = 1; i < array.length; i++) { if (array[i] > max) { max = array[i]; } } int time = 0; //判断位数; while (max > 0) { max /= 10; time++; } //建立10个队列; List<ArrayList> queue = new ArrayList<ArrayList>(); for (int i = 0; i < 10; i++) { ArrayList<Integer> queue1 = new ArrayList<Integer>(); queue.add(queue1); } //进行time次分配和收集; for (int i = 0; i < time; i++) { //分配数组元素; for (int j = 0; j < array.length; j++) { //得到数字的第time+1位数; int x = array[j] % (int) Math.pow(10, i + 1) / (int) Math.pow(10, i); ArrayList<Integer> queue2 = queue.get(x); queue2.add(array[j]); queue.set(x, queue2); } int count = 0;//元素计数器; //收集队列元素; for (int k = 0; k < 10; k++) { while (queue.get(k).size() > 0) { ArrayList<Integer> queue3 = queue.get(k); array[count] = queue3.get(0); queue3.remove(0); count++; } } }}

参考文章: 一遍记住java常用的八种排序算法与代码实现 八大排序算法Java java中常用的几种排序算法 必须知道的八大种排序算法【java实现】(一) 冒泡排序、快速排序 必须知道的八大种排序算法【java实现】(二) 选择排序,插入排序,希尔算法【详解】 必须知道的八大种排序算法【java实现】(三) 归并排序算法、堆排序算法详解


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表