十大排序算法,你真的都写得出来吗?

相信很多人都知道十大排序算法是哪些,但真正能手写出来的,可能只能完整的写出六七个。
怎么才能将排序算法都牢牢记住,在面试时丝毫不慌?
千万不要死记硬背,应该理解每个算法的特点,根据特点推演算法,才能稳如老狗。

分类

十大排序算法并非是毫无关联的十种算法,如果从逻辑层面上划分,可以归纳为以下五类:

  • 交换排序:通过两两交换元素实现排序
  • 选择排序:通过组内筛选元素实现排序
  • 插入排序:通过顺序插入元素实现排序
  • 归并排序:通过拆分合并元素实现排序
  • 桶类排序:通过分组匹配元素实现排序

交换排序

交换排序有:冒泡排序、快速排序
快速排序是冒泡排序的改进,虽然没有冒泡排序稳定,但在一般情况下效率更高

冒泡排序 - O(n2)

特点:相邻元素比较并交换

public void bubbleSort(int[] arr) {
    for (int i = 0; i < arr.length; i++) {
        for (int j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

快速排序 - O(nlogn)

特点:分治递归,线性划分(左右指针一轮遍历,将比目标数小的放左边,大的放右边)

public void quickSort(int[] arr, int row, int high) {
    if (row < high) {
        int target = partition(arr, row, high);
        quickSort(arr, row, target - 1);
        quickSort(arr, target + 1, high);
    }
}

public int partition(int[] arr, int low, int high) {
    int temp = arr[low];
    while (low < high) {
        while (low < high && arr[high] >= temp) high--;
        arr[low] = arr[high];
        while (low < high && arr[low] <= temp) low++;
        arr[high] = arr[low];
    }
    arr[low] = temp;
    return low;
}

缺点:最坏情况下回归O(n2),解决办法是使用“线性时间选择算法”得到中位数作为patation的目标数,但实现复杂度较大,不容易记忆。

选择排序

选择排序有:选择排序、堆排序
堆排序和选择排序都不稳定,但堆排序的效率更高

选择排序 - O(n2)

特点:每次遍历求最大or最小

public void selectSort(int[] arr) {
  for (int i = 0; i < arr.length - 1; i++) {
        int minIndex = i;
        for (int j = i + 1; j < arr.length; j++) {
            if (arr[minIndex] > arr[j]) {
                minIndex = j;
            }
        }
        if (i != minIndex) {
            int temp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }
    }
}

堆排序 - O(nlogn)

特点:将数组视为完全二叉树,父子大小关系决定升序or降序;第一轮生成堆,第二轮根与叶子做交换并调整

public void heapSort(int[] arr) {
    int len = arr.length;
    for (int i = len / 2 - 1; i >= 0; i--) {
        adjustment(arr, i, len);
    }
    for (int i = len - 1; i >= 0; i--) {
        int temp = arr[0];
        arr[0] = arr[i];
        arr[i] = temp;
        adjustment(arr, 0, i);
    }
}

public void adjustment(int[] arr, int pos, int len) {
    int child = pos * 2 + 1;
    if (child + 1 < len && arr[child] < arr[child + 1]) {
        child++;
    }
    if (child < len && arr[pos] < arr[child]) {
        int temp = arr[pos];
        arr[pos] = arr[child];
        arr[child] = temp;
        adjustment(arr, child, len);
    }
}

插入排序

插入排序有:插入排序、希尔排序
希尔排序是插入排序的改进,虽然没有插入排序稳定,但在一般情况下效率更高

插入排序 - O(n2)

特点:原地维护一个有序子数组,将新元素插入符合顺序的位置

public void insertSort(int[] arr) {
    for (int i = 1; i < arr.length; i++) {
        for (int j = i; j > 0; j--) {
            if (arr[j] < arr[j - 1]) {
                int temp = arr[j];
                arr[j] = arr[j - 1];
                arr[j - 1] = temp;
            }
        }
    }
}

二分版本

public void insertSort(int[] arr) {
	for (int i = 1; i < arr.length; i++) {
        int target = binarySearch(arr, i);
        int temp = arr[i];
        for (int j = i; j > target; j--) {
            arr[j] = arr[j - 1];
        }
        arr[target] = temp;
    }
}

public int binarySearch(int[] arr, int end) {
    int left = 0;
    int right = end - 1;
    while (left <= right) {
        int mid = (right + left) >> 1;
        if (arr[mid] < arr[end]) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return left;
}

希尔排序 - O(nlogn)

特点:按间隔分组,每组内进行插入排序(个人认为最难以意会的排序思想)

public void shellSort(int[] arr) {
    for (int gap = arr.length / 2; gap > 0; gap /= 2) {
        for (int i = gap; i < arr.length; i++) {
            int temp = arr[i];
            int j = i - gap;
            while (j >= 0 && arr[j] > temp) {
                arr[j + gap] = arr[j];
                j -= gap;
            }
            arr[j + gap] = temp;
        }
    }
}

归并排序

归并排序有:归并排序
归并排序不仅稳定,而且时间复杂度在任何情况下都一样是O(nlogn)

归并排序 - O(nlogn)

特点:向下分治递归,向上合并

public void mergeSort(int[] arr, int low, int high) {
 	if (low < high) {
        int mid = (low + high) >> 1;
        mergeSort(arr, low, mid);
        mergeSort(arr, mid + 1, high);
        merge(arr, low, mid, high);
    }
}

public void merge(int[] arr, 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 (arr[i] < arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
        }
    }
    while (i <= mid) {
        temp[k++] = arr[i++];
    }
    while (j <= high) {
        temp[k++] = arr[j++];
    }
    for (k = 0; k < temp.length; k++) {
        arr[low + k] = temp[k];
    }
}

桶类排序

桶类排序有:桶排序、基数排序、计数排序
桶类排序的优点是近乎于O(n)的效率且都很稳定,但都需要额外空间。其中计数排序最简单,是特殊的桶排序

桶排序 - O(n+m)

特点:按固定长度区间划分为桶,按桶匹配,桶内再排序

public void bucketSort(int[] arr) {
    int max = Integer.MIN_VALUE;
    int min = Integer.MAX_VALUE;
    for (int num : arr) {
        max = Math.max(max, num);
        min = Math.min(min, num);
    }
    int gap = (max - min) / 10 + 1;
    List[] bucket = new ArrayList[gap];
    for (int i = 0; i < gap; i++) {
        bucket[i] = new ArrayList<Integer>();
    }
    for (int num : arr) {
        bucket[(num - min) / gap].add(num);
    }
    int index = 0;
    for (int i = 0; i < gap; i++) {
        bucket[i].sort(null);
        for (int j = 0; j < bucket[i].size(); j++) {
            arr[index++] = (int)bucket[i].get(j);
        }
    }
}

缺点:不适用于数据范围大且数据值集中的数据

基数排序 - O(n*m)

特点:按0~9固定10个桶,按个位、十位、百位…确定需要m轮桶匹配

public void radixSort(int[] arr) {
    int max = Integer.MIN_VALUE;
    for (int num : arr) {
        max = Math.max(num, max);
    }
    int radix = 0;
    while (max != 0) {
        max /= 10;
        radix++;
    }
    int[][] bucket = new int[10][arr.length];
    int[] count = new int[10];
    for (int i = 0, pos = 1; i < radix; i++, pos *= 10) {
        for (int num : arr) {
            int mod = (num / pos) % 10;
            bucket[mod][count[mod]] = num;
            count[mod]++;
        }
        int index = 0;
        for (int k = 0; k < 10; k++) {
            if (count[k] > 0) {
                for (int l = 0; l < count[k]; l++) {
                    arr[index++] = bucket[k][l];
                }
                count[k] = 0;
            }
        }
    }
}

缺点:不适用于位数差距很小的数据

计数排序 - O(n+m)

特点:数据范围多大就有多少个桶

public void countSort(int[] arr) {
    int max = Integer.MIN_VALUE;
    int min = Integer.MAX_VALUE;
    for (int num : arr) {
        max = Math.max(max, num);
        min = Math.min(min, num);
    }
    int[] count = new int[max - min + 1];
    for (int num : arr) {
        count[num - min]++;
    }
    int index = 0;
    for (int i = 0; i < count.length; i++) {
        while (count[i] > 0) {
            arr[index++] = i + min;
            count[i]--;
        }
    }
}

缺点:不适用于数据范围大、稀疏的数据

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值