很久之前自己为了实习招聘就编程并梳理了八大排序算法,但一直没有时间写博客,今天趁着将要下班的闲暇之余,写篇博客来再梳理下
八大排序:
八大排序有分成了五类排序:
(1)交换排序:冒泡排序,快速排序
(2)选择排序:简单选择排序,堆排序
(3)插入排序:直接插入排序,希尔排序
(4)归并排序
(5)基数排序
一、冒泡排序(时间复杂度为O(n^2))
先来看下我们第一种排序方法,也是考的最常用的冒泡排序。
冒泡排序的思路:
(1)先从左边开始两两比较,如果逆序则交换,这样一轮就将最大的或者最小的分割到最右边或者最左边
(2)将得到的最大的和最小的值分隔开,对剩下的值在进行排序,直到最剩下一个数则结束
ps:就是两层for循环,外层控制每轮,内层控制每轮每相邻两个的比较,注意每轮后数组的个数要减少一个
代码:
public static int[] BubbleSort(int[] array){
int temp;
for (int i = 0; i < array.length-1; i++) {
for (int j = 0; j <array.length-1-i; j++) {
if(array[j]>array[j+1]){
temp = array[j];
array[j]=array[j+1];
array[j+1]=temp;
}
}
}
return array;
}
二、快速排序(时间复杂度为O(N*logN))
快速排序也是我们常考的一种排序方法!
快速排序的思路:
选取最左边的值为标杆,从左右同时开始,右边找比标杆小的值,左边找比标杆大的值,找到后即交换,然后继续找,直到左右两边的指针相遇则结束,然后将最左边的标杆和相遇的地方的值交换,这样再对左右的进行递归快排(先从右边找,在从左找)
2856194->2156894->1256894
再对两边分别进行快排,直到有序
代码:
public static void QuickSort(int[] array,int left,int right){
if(left>right)
return;
int i=left;
int j=right;
int temp = array[i];
while(i<j){
while(temp<array[j]&&i<j){//找右边比哨兵小的值
j--;
}
while(temp>=array[i]&&i<j){//找左边比哨兵大的值
i++;
}
if(i<j){//找到后满足条件则交换
int t =array[j];
array[j]=array[i];
array[i]=t;
}
}
array[left]=array[j];//每次交换完了,再交换哨兵
array[j]=temp;
QuickSort(array,left,j-1);//递归左半数组
QuickSort(array,j+1,right);//递归右半数组
}
注意:一定是先从右边开始找比标杆小的值,再从左边开始找比标杆大的值
三、简单选择排序(时间复杂度为O(n^2))
思路:从数组中找出最小值,记下坐标,然后跟第一个进行交换,然后把第一个剥离,对然后对剩下的数组找出最小的,再跟第二个交换,直到只剩下一个值即结束
ps:一定要记下坐标,这个较为简单
代码:
public static int[] SelectionSort(int[] array){
int temp;
for (int i = 0; i < array.length-1; i++) {
int k=i;
for (int j = i+1; j <array.length; j++) {
if(array[k]>array[j]){
k=j;
}
}
temp=array[i];
array[i]=array[k];
array[k]=temp;
}
return array;
}
四、堆排序(时间复杂度为O(N*logN))
堆排序就是将数组形成一个大根堆,堆顶是数值最大的,然后再跟最末尾的最小值进行交换,剥离出最后的那个最大值,然后再对堆进行调整为一个大根堆,在继续迭代下去。
ps:这个排序比较难,如何构建大根堆,以及堆如何排序都需要我们进行多思考
代码:
import java.util.Arrays;
/**
* 堆排序demo
*/
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));
}
public 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);//重新对堆进行调整
}
}
/**
* 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
*/
public static void adjustHeap(int []arr,int i,int length){
int temp = arr[i];//先取出当前元素i
for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点
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;
}
}
五、直接插入排序(时间复杂度为O(n^2))
思路:第二个跟第一个比较,逆序则交换,第三个跟第二个比较,逆序则再跟第一个比较,逆序就插入到第一个之前,然后之后的分别跟前面的有序数组依次比较,直到找到比自己小的,则插入到他后面
ps:其实也是两层for循环,外层控制第N个进行插入比较,内层控制跟第N-1个进行…第1个比较,找到自己的位置,然后交换,因为是数组涉及到后面的移动,我们默认如果小于则交换,然后再跟后面的比较
代码:
public static int[] InsertSort(int[] array){
int temp;
for (int i = 1; i < array.length; i++) {
int k=i;//注意用一个变量来记第几个
for (int j = i-1; j >= 0; j--) {
if (array[k] < array[j]) {
temp=array[k];
array[k]=array[j];
array[j]=temp;
k--;
}
}
}
return array;
}
六、希尔排序(时间复杂度为O(N*logN))
希尔排序是插入排序的改良版,并不是每个都比较
思路:
(1)步长设为数组长的一半,然后第n/2+1的值跟第一个比较,如果逆序则交换,如果交换,然后再跟前面步长的值比较(如果没有就下一个),一直比较完一轮,
(2)然后步长变为原来的一半,然后继续比较,直到步长变为1比较完,就结束
ps:步长的选择很重要,一般开始的步长都等于数组长度的一半
代码:
public static void ShellSort(int[] array){
if(array == null || array.length <= 1){
return;
}
int num=array.length/2;
while(num>=1){
for (int i =num;i<array.length; i++) {
for (int j =i-num; j>=0; j-=num) {
if(array[j]>array[j+num]){
int temp=array[j];
array[j]=array[j+num];
array[j+num]=temp;
}
}
}
num/=2;
}
}
注意:双层循环,可以从后面开始,也可以从步长地方开始比较
七、归并排序(时间复杂度为O(N*logN))
思路:
(1)先让数组中的每个数成为单独的区间,然后相邻两个区间进行比较排序后并合并
(2)然后从1个变成2个,2个变成4个,直到变成一个区间
代码:
public static int[] MergeSort(int[] array,int left,int right) {
if (left < right) {
int mid = (left + right)/2;
MergeSort(array, left, mid);
MergeSort(array, mid + 1, right);
Merge(array, left, mid, right);
}
return array;
}
public static int[] Merge(int[] array,int left,int mid,int right){//合并
int[] a =new int[right-left+1];
int i=left;
int j=mid+1;
int k=0;
while(i<=mid&&j<=right){
if(array[i]<array[j]){
a[k++]=array[i++];
}else{
a[k++]=array[j++];
}
}
while(i<=mid){
a[k++]=array[i++];
}
while(j<=right){
a[k++]=array[j++];
}
for (int l = 0; l<a.length;l++,k++) {
array[l+left]=a[l];
}
return array;
}
八、基数排序(时间复杂度为O(N))
比较复杂,
思路:将所有数补成相同位数的数,往前面补0,然后找0-9个桶,先开始对个位进行排序,然后把数取出(0-9),然后从序列中对十位排序,然后把数从桶拿出,再对百位排序,直到排到最高位数则结束
代码:
/*
* 获取数组a中最大值
*
* 参数说明:
* a -- 数组
* n -- 数组长度
*/
int get_max(int a[], int n)
{
int i, max;
max = a[0];
for (i = 1; i < n; i++)
if (a[i] > max)
max = a[i];
return max;
}
/*
* 对数组按照"某个位数"进行排序(桶排序)
*
* 参数说明:
* a -- 数组
* n -- 数组长度
* exp -- 指数。对数组a按照该指数进行排序。
*
* 例如,对于数组a={50, 3, 542, 745, 2014, 154, 63, 616};
* (01) 当exp=1表示按照"个位"对数组a进行排序
* (02) 当exp=10表示按照"十位"对数组a进行排序
* (03) 当exp=100表示按照"百位"对数组a进行排序
* ...
*/
void count_sort(int a[], int n, int exp)
{
int output[n]; // 存储"被排序数据"的临时数组
int i, buckets[10] = {0};
// 将数据出现的次数存储在buckets[]中
for (i = 0; i < n; i++)
buckets[ (a[i]/exp)%10 ]++;
// 更改buckets[i]。目的是让更改后的buckets[i]的值,是该数据在output[]中的位置。
for (i = 1; i < 10; i++)
buckets[i] += buckets[i - 1];
// 将数据存储到临时数组output[]中
for (i = n - 1; i >= 0; i--)
{
output[buckets[ (a[i]/exp)%10 ] - 1] = a[i];
buckets[ (a[i]/exp)%10 ]--;
}
// 将排序好的数据赋值给a[]
for (i = 0; i < n; i++)
a[i] = output[i];
}
/*
* 基数排序
*
* 参数说明:
* a -- 数组
* n -- 数组长度
*/
void radix_sort(int a[], int n)
{
int exp; // 指数。当对数组按各位进行排序时,exp=1;按十位进行排序时,exp=10;...
int max = get_max(a, n); // 数组a中的最大值
// 从个位开始,对数组a按"指数"进行排序
for (exp = 1; max/exp > 0; exp *= 10)
count_sort(a, n, exp);
}
又梳理了一遍,顿时清晰多了,排序算法还是非常重要的,要经常熟悉