排序算法主要有:插入排序,选择排序,冒泡排序,希尔排序,归并排序,快速排序,堆排序。
插入排序
基本思想:每一步都将一个待排数据按其大小插入到已经排序的数据中的适当位置,直到全部插入完毕。
原始:4 3 1 2
1) 3 4 1 2
2) 1 3 4 2
3) 1 2 3 4
/** * 插入排序 */ public static int[] insertSort(int[] arr) { for (int i = 1; i < arr.length; i++) { int index = i;// index当前扫描到的元素下标 int temp = arr[i]; // 寻找插入的位置 while (index > 0 && temp < arr[index - 1]) { arr[index] = arr[index - 1]; index--; } arr[index] = temp; } return arr; }
选择排序
基本思想:从所有序列中先找到最小的,然后放到第一个位置。之后再看剩余元素中最小的,放到第二个位置……以此类推,就可以完成整个的排序工作了。可以很清楚的发现,选择排序是固定位置,找元素。相比于插入排序的固定元素找位置,是两种思维方式。
3 2 1 4 6 5
初始化索引位置为0
寻找最小值所在位置交换:1 2 3 4 6 5
初始化索引位置为1
寻找最小值所在位置交换:1 2 3 4 6 5
依次类推!
/** * 选择排序 */ public static int[] selectSort(int[] arr) { for (int i = 0; i < arr.length; i++) { int minVal = arr[i]; int index = i; for (int j = i + 1; j < arr.length; j++) {// 找到最小元素 if (arr[j] < minVal) { minVal = arr[j]; index = j; } } arr[index] = arr[i]; arr[i] = minVal; } return arr; }
冒泡排序
基本思想:原理是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换。
/** * 冒泡排序 * * @param arr * 输入的待排数组 * @return 返回排序号的数组 */ public static int[] bubbleSort(int[] arr) { for (int i = 0; i < arr.length; i++) { for (int j = 1; j < arr.length; j++) { if (arr[j - 1] > arr[j]) { int temp = arr[j - 1]; arr[j - 1] = arr[j]; arr[j] = temp; } } } return arr; }
希尔排序
基本思想:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量 =1( < …<d2<d1),即所有记录放在同一组中进行直接插入排序为止。(下图来自百度图片)
/** * 希尔排序 * * @author sgl * */ public class ShellSort { public static int[] shellSort(int[] arr) { int step = arr.length / 2;// 初始步长 while (1 <= step) { for (int i = step; i < arr.length; i++) { if (arr[i] < arr[i - step]) { int temp = arr[i]; arr[i] = arr[i - step]; arr[i - step] = temp; } } step = step / 2; for (int i = 0; i < arr.length; i++) { System.out.print(arr[i]+","); } System.out.println(); } return arr; }
归并排序
基本思想:将待排序序列R[0...n-1]看成是n个长度为1的有序序列,将相邻的有序表成对归并,得到n/2个长度为2的有序表;将这些有序序列再次归并,得到n/4个长度为4的有序序列;如此反复进行下去,最后得到一个长度为n的有序序列。
归并排序其实要做两件事:
(1)“分解”——将序列每次折半划分。
(2)“合并”——将划分后的序列段两两合并后排序。
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; } 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]; } } }
快速排序
基本思想:快速排序采用的思想是分治思想。
快速排序是找出一个元素(理论上可以随便找一个)作为基准,然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的元素值 都不小于基准值,如此作为基准的元素调整到排序后的正确位置。递归快速排序,将其他n-1个元素也调整到排序后的正确位置。最后每个元素都是在排序后的正 确位置,排序完成。所以快速排序算法的核心算法是分区操作,即如何调整基准的位置以及调整返回基准的最终位置以便分治递归。
package com.sgl.alg; /** * 快速排序 * * @author sgl * */ public class QuickSort { static void quicksort(int n[], int left, int right) { int dp; if (left < right) { dp = partition(n, left, right); quicksort(n, left, dp - 1); quicksort(n, dp + 1, right); } } static int partition(int n[], int left, int right) { int pivot = n[left]; while (left < right) { while (left < right && n[right] >= pivot) right--; if (left < right) n[left++] = n[right]; while (left < right && n[left] <= pivot) left++; if (left < right) n[right--] = n[left]; } n[left] = pivot; return left; } }
堆排序
基本思想:堆排序是利用堆的性质进行的一种选择排序。
利用大顶堆(小顶堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。
其基本思想为(大顶堆):
1)将初始待排序关键字序列(R1,R2....Rn)构建成大顶堆,此堆为初始的无须区;
2)将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n];
3)由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,......Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
2.
堆排序是一种树形选择排序,是对直接选择排序的有效改进。
堆的定义下:具有n个元素的序列 (h1,h2,...,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,...,n/2)时称之为堆。在这里只讨论满足前者条件的堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项(大顶堆)。完全二 叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。
思想:初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个 堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点的堆,并对 它们作交换,最后得到有n个节点的有序序列。从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。
3。实例
初始序列:46,79,56,38,40,84
建堆:
交换,从堆中踢出最大数
依次类推:最后堆中剩余的最后两个结点交换,踢出一个,排序完成。
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; } }