排序算法应该是最基础的算法了,博主面试至今没有面试官单就一个排序算法让写个代码什么的。本篇小博先对常见的排序算法进行一下总结,方便日后查看吧。主要内容包括:冒泡排序(快速排序)、选择排序(堆排序)、插入排序(希尔排序)、归并排序。
一、冒泡排序
代码如下:
public static int[] BubbleSort(int[] a) {
if(a == null || a.length < 2)
return a;
for(int i = 0; i < a.length-1; i++) {
int flag = 0;
for(int j = a.length-1; j > i; j--) {
if(a[j] < a[j-1]) {
int temp = a[j];
a[j] = a[j-1];
a[j-1] = temp;
flag = 1;
}
}
if(flag == 0)
break;
}
return a;
}
二、选择排序
思路:两个循环变量i和j,一个最小值指针min,外层循环从0开始,min每次都初始化为i,内层循环j从i+1开始,其性能略优于冒泡排序法,代码如下:
public static int[] SelectSort(int[] a) {
if(a == null || a.length < 2)
return a;
for(int i = 0; i < a.length-1; i++) {
int min = i;
boolean change = false;
for(int j = i+1; j < a.length; j++) {
if(a[j] < a[min]) {
min = j;
change = true;
}
}
if(change) { //a[i]恰好就是当前序列中最小的那个
int temp = a[i];
a[i] = a[min];
a[min] = temp;
}
}
return a;
}
三、插入排序
思路:遍历数组,将数组后面的数按照顺序插入到正确的位置,代码如下
public static int[] InsertSort(int[] a) {
if(a == null || a.length < 2)
return a;
for(int i = 1; i < a.length; i++) {
int j = i;
while(j > 0 && a[j] < a[j-1]) {
int temp = a[j];
a[j] = a[j-1];
a[j-1] = temp;
j--;
}
}
return a;
}
四、希尔排序
思路:分组的插入排序,代码如下:
public static int[] ShellSort(int[] a) {
<span style="white-space:pre"> </span>if(a == null || a.length < 2)
<span style="white-space:pre"> </span>return a;
<span style="white-space:pre"> </span>for(int increment = a.length/3; increment > 0; increment /= 3) { //确定增量
<span style="white-space:pre"> </span>for(int i = increment; i < a.length; i+=increment) {
<span style="white-space:pre"> </span>int j = i;
<span style="white-space:pre"> </span>while(j > 0 && a[j] < a[j-increment]) {
<span style="white-space:pre"> </span>int temp = a[j];
<span style="white-space:pre"> </span>a[j] = a[j-increment];
<span style="white-space:pre"> </span>a[j-increment] = temp;
<span style="white-space:pre"> </span>j-=increment;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
return a;
<span style="white-space:pre"> </span>}
五、堆排序
关键问题:怎么将一个数列变成为一个大顶堆,其实质就是从下往上、从右到左将每个非终端节点当做根节点,将其和其子树调整成大顶堆,代码如下
public static void HeapAdjust(int[] a, int s, int m) {
<span style="white-space:pre"> </span>int temp = a[s];
<span style="white-space:pre"> </span>for(int i = 2*s+1; i < m; i = i*2+1) { //2*s+1是左孩子的下标
<span style="white-space:pre"> </span>if(i < m && a[i] < a[i+1]) //左孩子 < 右孩子
<span style="white-space:pre"> </span>++i; //i就变成了右孩子的下标
<span style="white-space:pre"> </span>if(temp >= a[i]) //如果根节点大于孩子节点了 break
<span style="white-space:pre"> </span>break;
<span style="white-space:pre"> </span>a[s] = a[i]; //根节点值变成较大的孩子节点
<span style="white-space:pre"> </span>s = i; //下次循环的根节点变成这次的较大孩子节点
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>a[s] = temp; //较大孩子节点处的值变成根节点
<span style="white-space:pre"> </span>} //程序本质就是完成根节点与孩子节点的交换,选出最大值作为新的根节点
public static int[] HeapSort(int[] a) {
if(a == null || a.length < 2)
return a;
for(int i = a.length/2; i > 0; i--) { //先完成堆创建,最大值跑到根节点
HeapAdjust(a,i-1,a.length-1);
}
for(int i = a.length-1; i > 1; i--) {
int temp = a[i];
a[i] = a[0];
a[0] = temp;
HeapAdjust(a,0,i-1);
}
return a;
}
六、归并排序
主要用到了合并两个有序数组为一个有序数组,递归调用,代码如下:
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/j已经 >mid/high了
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];
}
}
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 int[] MergeSort(int[] a) {
if(a == null || a.length < 2) {
return a;
}
return sort(a,0,a.length-1);
}
七、快速排序
//基本思想:选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,
//一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分。
//快速排序是不稳定的排序。
//快速排序的时间复杂度为O(nlogn)。
//当n较大时使用快排比较好,当序列基本有序时用快排反而不好。
public static int getMiddle(int[] a,int low,int high) { //完成选取基值,左右选边的过程
int temp = a[high]; //基值的位置
while(low < high) {
while(low < high && a[high] > temp) {
high--;
}
while(low < high && a[low] < temp) {
low++;
}
int t = a[low];
a[low] = a[high];
a[high] = t;
}
return high;
}
public static void qsort(int[] a,int low,int high) {
if(low < high) {
int mid = getMiddle(a,low,high);
qsort(a,low,mid-1);
qsort(a,mid+1,high);
}
}
public static void quickSort(int[] a,int low,int high) {
if(a.length > 0) {
qsort(a,0,a.length-1);
}
}
}