1、堆排序
堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
package cn.ctgu.offer.sort;
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[]arr= {9,8,7,6,5,4,3,2,1};
sort(arr);
System.out.println(Arrays.toString(arr));
}
private static void sort(int[] arr) {
//1、构建大根堆
for(int i=arr.length/2-1;i>=0;i--) {
//从第一个非叶子结点开始从下到上,从右至左进行调整结构
adjustHeap(arr,i,arr.length);
}
//2、调整堆结构+交换堆顶元素与末尾元素
for(int j=arr.length-1;j>0;j--) {
swap(arr,0,j);//将堆顶元素与末尾元素进行交换
adjustHeap(arr,0,j);//重新对堆进行调整
}
}
//调整大根堆
private static void adjustHeap(int[] arr, int i, int length) {
int temp=arr[i];//先取出当前元素i
for(int k=i*2+1;k<length;k=2*k+1) {//从i结点的左子结点开始,也就是2i+1处开始
if(k+1<length&&arr[k]<arr[k+1]) {//如果左子结点小于右子结点,则从右子结点开始,因为我们要调整的目的是使得根结点大于左右子结点
k++;
}
if(arr[k]>temp) {
arr[i]=arr[k];
i=k;
}else {
break;
}
}
arr[i]=temp;//将temp值放到最终的位置
}
//交换元素
public static void swap(int[]arr,int a,int b) {
int temp=arr[a];
arr[a]=arr[b];
arr[b]=temp;
}
}
再简单总结下堆排序的基本思路:
a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
b.将堆顶元素与末尾元素交换,将最大元素”沉”到数组末端;
c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
降序采用小根堆,升序采用大根堆。
2、归并排序
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案”修补”在一起,即分而治之)。
package cn.ctgu.offer;
import java.util.Arrays;
/*
* 归并排序
*
* */
public class MergeSort {
public static void sort(int []arr) {
//排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
int[] temp=new int[arr.length];
sort(arr,0,arr.length-1,temp);
}
private static void sort(int[] arr, int left, int right, int[] temp) {
if(left<right) {
int mid=(left+right)/2;
//左边归并排序,使得左子序列有序
sort(arr,left,mid,temp);
//右边归并排序,使得右子序列有序
sort(arr,mid+1,right,temp);
//将两个有序子数组合并操作
merge(arr,left,mid,right,temp);
}
}
private static void merge(int[] arr, int left, int mid, int right, int[] temp) {
//左序列指针
int i=left;
//右序列指针
int j=mid+1;
//临时数组指针
int t=0;
while(i<=mid && j<right) {
if(arr[i]<=arr[j]) {
temp[t]=arr[i];
t++;
i++;
}else{
temp[t]=arr[j];
t++;
j++;
}
}
while(i<=mid) {//将左边剩余元素填充进temp中
temp[t]=arr[i];
t++;
i++;
}
while(j<=right) {//将右序列剩余元素填充进temp中
temp[t]=arr[j];
t++;
j++;
}
t=0;
//将temp中的元素全部拷贝到原数组中
while(left<=right) {
arr[left]=temp[t];
left++;
t++;
}
}
public static void main(String []args){
int []arr = {9,8,7,6,5,4,3,2,1};
sort(arr);
System.out.println(Arrays.toString(arr));
}
}
3、插入排序
直接插入排序基本思想是每一步将一个待排序的记录,插入到前面已经排好序的有序序列中去,直到插完所有元素为止。
package cn.ctgu.offer.sort;
import java.util.Arrays;
public class InsertionSort {
public static void sort(int[]arr) {
for(int i=1;i<arr.length;i++) {
int j=i;
while(j>0&&arr[j]<arr[j-1]) {
swap(arr,j,j-1);
j--;
}
}
}
private static void swap(int[] arr, int j, int i) {
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
public static void main(String[]args) {
int[]arr= {0,3,4,5,61,2};
InsertionSort solution=new InsertionSort();
solution.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
4、希尔排序
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
package cn.ctgu.offer.sort;
import java.util.Arrays;
public class ShellSort {
public static void main(String[]args) {
int[]arr= {1,4,2,7,9,8,3,6};
sort(arr);
System.out.println(Arrays.toString(arr));
}
//希尔排序,针对有序序列在插入时采用交换法
private static void sort(int[] arr) {
//增量gap,并逐步缩小增量
for(int gap=arr.length/2;gap>0;gap/=2) {
//从第gap个元素,逐个对其所在组进行插入排序操作
for(int i=gap;i<arr.length;i++) {
int j=i;
while(j-gap>=0&&arr[j]<arr[j-gap]) {
//插入排序采用交换法
swap(arr,j,j-gap);
j-=gap;
}
}
}
}
private static void swap(int[] arr, int j, int i) {
int tmp=arr[i];
arr[i]=arr[j];
arr[j]=tmp;
}
}
5、冒泡排序
原理:比较两个相邻的元素,将值大的元素交换至右端。
思路:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。重复第一趟步骤,直至全部排序完成。
第一趟比较完成后,最后一个数一定是数组中最大的一个数,所以第二趟比较的时候最后一个数不参与比较;
第二趟比较完成后,倒数第二个数也一定是数组中第二大的数,所以第三趟比较的时候最后两个数不参与比较;
依次类推,每一趟比较次数-1;
package cn.ctgu.offer.sort;
import java.util.Arrays;
public class BubbleSort {
public static void sort(int[]arr) {
for(int i=0;i<arr.length-1;i++) {//外层循环控制排序趟数
for(int j=0;j<arr.length-1-i;j++) {
if(arr[j]>arr[j+1]) {
int tmp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=tmp;
}
}
}
}
public static void main(String[]args) {
int[]arr= {6,3,8,2,9,1};
BubbleSort solution=new BubbleSort();
solution.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
6、选择排序
简单选择排序是最简单直观的一种算法,基本思想为每一趟从待排序的数据元素中选择最小(或最大)的一个元素作为首元素,直到所有元素排完为止,简单选择排序是不稳定排序。
在算法实现时,每一趟确定最小元素的时候会通过不断地比较交换来使得首位置为当前最小,交换是个比较耗时的操作。其实我们很容易发现,在还未完全确定当前最小元素之前,这些交换都是无意义的。我们可以通过设置一个变量min,每一次比较仅存储较小元素的数组下标,当轮循环结束之后,那这个变量存储的就是当前最小元素的下标,此时再执行交换操作即可。
package cn.ctgu.offer.sort;
import java.util.Arrays;
public class SelectSort {
public static void sort(int[]arr) {
for(int i=0;i<arr.length-1;i++) {
int min=i;////每一趟循环比较时,min用于存放较小元素的数组下标,这样当前批次比较完毕最终存放的就是此趟内最小的元素的下标,避免每次遇到较小元素都要进行交换。
for(int j=i+1;j<arr.length;j++) {
if(arr[j]<arr[min]) {
min=j;
}
}
//进行交换,如果min发生变化,则进行交换
if(min!=i) {
swap(arr,min,i);
}
}
}
private static void swap(int[] arr, int j, int i) {
int tmp=arr[i];
arr[i]=arr[j];
arr[j]=tmp;
}
public static void main(String[]args) {
int[]arr= {1,4,2,7,9,8,3,6};
sort(arr);
System.out.println(Arrays.toString(arr));
}
}
7、快速排序
该方法的基本思想是:
1.先从数列中取出一个数作为基准数。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。
package cn.ctgu.offer.sort;
import java.util.Arrays;
public class QuickSort {
public void sort(int[] arr,int l,int r) {
if(l<r) {
int i=l;
int j=r;
int x=arr[l];//基准值
while(i<j) {
while(i<j && arr[j]>=x) {//从右向左找到第一个小于x的数
j--;
}
if(i<j) {
arr[i]=arr[j];
i++;
}
while(i<j&& arr[i]<x) {//从左向右找到第一个大于x的数
i++;
}
if(i<j) {
arr[j]=arr[i];
j--;
}
arr[i]=x;
sort(arr,l,i-1);//递归调用
sort(arr,i+1,r);//递归调用
}
}
}
public static void main(String[]args) {
int a[]= {10,9,8,7,6,5,4,3,2,1};
QuickSort solution=new QuickSort();
solution.sort(a,0,9);
System.out.println(Arrays.toString(a));
}
}