排序算法合集
快速排序
我们可以把快速排序看着三个步骤:
1.选择基准值:在待排序列中,按照某种方式挑出一个元素,作为基准值。
2.分割操作:以该基准值在序列中的实际位置,把序列分成两个子序列,一边是比它大的值,另外一边是比它小的值。
3.递归:对两个子序列进行快排,直到序列为空或者只有一个元素。
过程演示
这是我画的一张图,结合这张图再看下面的代码可能会比较好理解一点,当然在看代码的时候最后可以自己画一张草图,可以熟悉一下整个过程,加深理解!
代码实现
public class QuickSort {
public static void main(String[] args) {
int[] arr = new int[] {
9,4,6,8,3,10,4,6};
quickSort(arr,0,arr.length - 1);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int[] arr,int low,int high) {
int p,i,j,temp;
if(low >= high) {
return;
}
//p就是基准数,这里就是每个数组的第一个
p = arr[low];
i = low;
j = high;
while(i < j) {
//右边当发现小于p的值时停止循环
while(arr[j] >= p && i < j) {
j--;
}
//这里一定是右边开始,上下这两个循环不能调换(下面有解析,可以先想想)
//左边当发现大于p的值时停止循环
while(arr[i] <= p && i < j) {
i++;
}
temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
//i=j,指针相遇,把较小的arr[i]赋值给arr[0],较大的p赋值给arr[i];
arr[low] = arr[i];//这里的arr[i]一定是小于p的,经过i、j交换后i处的值一定是小于p的(j先走)
//把最后一个交换的array[i]赋值给arr[low],即arry[0];
arr[i] = p; //把较大的p赋值给arr[i]
quickSort(arr,low,j-1); //对左边快排
quickSort(arr,j+1,high); //对右边快排
}
}
一些问题
1.什么是基准值:
其实就是在数组里面找一个数,一般选择数组的第一个数作为基准值,当然这不一定是最佳的基准值,但这不妨碍我们做快速排序。本篇只讲标配版的快排,所以就选第一位作为基准值,以后有机会再更新高配版的~
2.快排中为什么一定是右边先开始循环?
从右边先开始的前提是我们选择序列中最左边的元素最为基准值。
先从右边开始可以保证i,j相等的时候,arr[i] = arr[j] 小于基准值p。这样交换之后才能保证基准值左右两边分别小于和大于它的值。
我们图片演示一下:
可以发现如果左边先走的话将导致分组不成功,即左边的元素并不是都小于基准值。
冒泡排序
相邻的两个元素进行比较,交换
public static void main(String[] args) {
int[] score ={
7,5,4,2,3,8};
for (int i = 0; i < score.length - 1; i++) {
// 比较相邻两个元素,较大的数往后冒泡
for (int j = 0; j < score.length - 1 - i; j++) {
if (score[j] > score[j + 1]) {
double temp = score[j + 1]; // 把第一个元素值保存到临时变量中
score[j + 1] = score[j]; // 把第二个元素值转移到第一个元素变量中
score[j] = temp; // 把临时变量(第一个元素的原值)保存到第二个元素中
}
}
}
}
优化版
public static void main(String[] args) {
int[] arr = {
1, 3, 4, 2, 6, 7, 8, 0, 5 };
int i = 0;
int tmp = 0;
Boolean flag = true;
int pos = 0;// 用来记录最后一次交换的位置
int k = arr.length - 1;
for (i = 0; i < arr.length - 1; i++) {
pos = 0;
int j = 0;
flag = true;
for (j = 0; j < k; j++) {
if (arr[j] > arr[j + 1]) {
// 交换
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
flag = false;// 加入标记
pos = j;// 交换元素,记录最后一次交换的位置
}
}
if (flag == true)// 如果没有交换过元素,则已经有序
{
continue;
}
k = pos;// 下一次比较到记录位置即可
}
long endTime = System.nanoTime(); // 获取结束时间
}
选择排序
从第一个位置的元素开始,与他后边的每一个元素进行比较,交换,把当前认为最小的元素放在该位置(第一个位置)
int[] nums = {
84,83,88,87,61};
for(int i = 0;i< nums.length-1;i++){
for(int j=i;j< nums.length-1;j++){
if(nums[i] > nums[j+1]){
int temp ;
temp = nums[i];
nums[i] = nums[j+1];
nums[j+1] = temp;
}
}
}
堆排序
堆排序(Heapsort) 是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
实现:
根据传进来的数组创建一个最小堆,最小堆的第一个元素一定是最小的,所以我们每次删除根节点(数组第一个元素),把根节点放入我们新建的数组中,最后得到的新数组就是一个排好序的。
注意:每次删除操作不仅仅是删除根节点,删除之后要调整数组,使数组重新满足最小堆的要求
java提供了一个数据类型PriorityQueue-优先级队列,使用动态数组实现了最小堆,我们可以直接用
//数据结构-最小堆
static int[] Heap_Sort(int[] array){
int len = array.length;
//创建最小堆,PriorityQueue内部维护的数组默认长度是11
//为防止给的数组长度大于11,进堆时要扩展内部数组的长度
//所以初始化时,确定内部数组的长度等于我们传入的数组的长度
Queue<Integer> minHeap = new PriorityQueue<>(len);
for(int e : array){
minHeap.add(e);
}
//把堆顶(最小元素)投出,存在新数组中
for(int i = 0; i < len; i++){
array[i] = minHeap.poll();
}
return array;
}
归并排序
package algorithms;
public class myMergeSort {
static int number=0;
public static void main(String[] args) {
int[] a = {
26, 5, 98, 108, 28, 99, 100, 56, 34, 1 };
printArray("排序前:",a);
MergeSort(a);
printArray("排序后:",a);
}
private static void printArray(String pre,int[] a) {
System.out.print(pre+"\n");
for(int i=0;i<a.length;i++)
System.out.print(a[i]+"\t");
System.out.println();
}
private static void MergeSort(int[] a) {
// TODO Auto-generated method stub
System.out.println("开始排序");
Sort(a, 0, a.length - 1);
}
private static void Sort(int[] a, int left, int right) {
if(left>=right)
return;
int mid = (left + right) / 2;
//二路归并排序里面有两个Sort,多路归并排序里面写多个Sort就可以了
Sort(a, left, mid);
Sort(a, mid + 1, right);
merge(a, left, mid, right);
}
private static void merge(int[] a, int left, int mid, int right) {
int[] tmp = new int[a.length];
int r1 = mid + 1;
int tIndex = left;
int cIndex=left;
// 逐个归并
while(left <=mid && r1 <= right) {
if (a[left] <= a[r1])
tmp[tIndex++] = a[left++];
else
tmp[tIndex++] = a[r1++];
}
// 将左边剩余的归并
while (left <=mid) {
tmp[tIndex++] = a[left++];
}
// 将右边剩余的归并
while ( r1 <= right ) {
tmp[tIndex++] = a[r1++];
}
System.out.println("第"+(++number)+"趟排序:\t");
// TODO Auto-generated method stub
//从临时数组拷贝到原数组
while(cIndex<=right){
a[cIndex]=tmp[cIndex];
//输出中间归并排序结果
System.out.print(a[cIndex]+"\t");
cIndex++;
}
System.out.println();
}
}
插入排序
public static void main(String[] args){
int[] nums = buildArray();
System.out.print("亲,您输入的初始数组是 : ");
printArray(nums);
//默认构造从左→右依次递增的序列
for(int i=1; i<nums.length; i++){
int j;
int temp = nums[i]; //temp是本趟待插入的数,之前从0~i-1的数全是从左→右有序递增。
for(j=i-1; j>=0&&nums[j]>temp; j--){
nums[j+1] = nums[j];
}
nums[j+1] = temp;
System.out.print("第"+i+"次直接插入排序后的数组:");
printArray(nums);
}
}
希尔排序
希尔排序是在插入排序的基础上优化而来的,原理就是将一个数组按一定间隔分割成若干个小区间,针对每个区间使用插入排序。当各个区间都有序时再针对整体进行插入排序。这样整个数组就是有序的,而且效率很高
- 以 32 43 24 18 49 28 10 59 18 41 序列为例
- 第一次 h= 10 / 2 = 5;则从第6个元素开始,依次与前面相距间隔为5的元素比较
32 43 24 18 49 *28* 10 59 18 41 —> *28* 43 24 18 49 32 10 59 18 41
28 43 24 18 49 32 *10* 59 18 41 —> 28 *10* 24 18 49 32 *43* 59 18 41
28 10 24 18 49 32 43 59 18 41 —> 28 10 *24* 18 49 32 43 59 18 41
28 10 24 *18* 49 32 43 59 18 41 —> 28 10 24 18 49 32 43 59 *18* 41
28 10 24 18 *49* 32 43 59 18 *41* —> 28 10 24 18 *41* 32 43 59 18 *49*
- 这样第一趟就得到 28 10 24 18 41 32 43 59 18 49 的序列,后面再缩小 h = h/ 2 = 2,同上述步骤一样依次与前面相距间隔为 2 的元素就行比较。
希尔排序很适合用于大数组排序,h的取值也是提高希尔排序效率的关键因子。
import java.util.Arrays;
// 希尔排序对于大型数组排序效率很高,不需要额外的内存空间
public class 希尔排序 {
public static void shellSort(int[] a) {
int n = a.length;
// 设置增量,增量的取法有很多,这里是推荐取法
int h = 1;
while (h < n / 3) {
h = 3 * h + 1;
}
// 增量最小为 1 ,也就是相邻的两个元素比较
while (h >= 1) {
// 对相距 h 的两个元素插入排序
for (int i = h; i < n; i++) {
for (int j = i; j >= h && less(a[j], a[j-h]); j -= h) {
exch(a, j, j-h);
}
}
// 排序结束后,缩小增量,继续排序
h /= 3;
}
}
private static void exch(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
private static boolean less(Comparable v, Comparable w) {
// 若 v 小于 w 则返回负数
return v.compareTo(w) < 0;
}
public static void main(String[] args) {
int[] s = {
4, 6, 1, 2, 3, 0};
System.out.println(Arrays.toString(s));
shellSort(s);
System.out.println(Arrays.toString(s));
}
}