概述
1.直接插入排序
1.1基本思想
将一个记录插入到已经排序好的有序表中,从而得到一个新的表,记录数增1的有序表
1.2示例
1.3代码实现
public static void insertSort(int [] nums){
for(int i = 1; i < nums.length; i++){
int temp = nums[i];
int j = i - 1;
for(; j >= 0; j--){
if(nums[j] > temp){
nums[j+1] = nums[j];
}else{
break;
}
}
nums[j+1] = temp;
}
}
2.希尔排序
2.1基本思想
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
2.2具体方法
- 希尔排序是对直接插入排序的优化。
- 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很
快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
2.3示例
2.4代码实现
public static void shellSort(int [] nums){
int gap = nums.length;
while(gap > 1){
shell(nums,gap);
gap = (gap/3)+1;
}
shell(nums,1);
}
public static void shell(int [] nums, int gap){
for(int i = gap; i < nums.length; i++){
int temp = nums[i];
int j = i - gap;
for(;j >= 0; j = j - gap){
if(nums[j] > temp){
nums[j+gap] = nums[j];
}else{
break;
}
}
nums[j+gap] = temp;
}
}
3.简单选择排序
3.1基本思想
每一次从无序区间选出最大(或最小)的一个元素,存放在无序区间的最后(或最前),直到全部待排序的数据元素排完 。
3.2具体方法
-
第一趟,从n 个记录中找出关键码最小的记录与第一个记录交换;
-
第二趟,从第二个记录开始的n-1 个记录中再选出关键码最小的记录与第二个记录交换;
-
以此类推…
-
第i 趟,则从第i 个记录开始的n-i+1 个记录中选出关键码最小的记录与第i 个记录交换,直到整个序列按关键码有序。
3.3示例
3.4代码实现
public static void selection(int [] nums){
for (int i = 0; i < nums.length - 1; i++) {
int j = i + 1;
int index = i;
for(; j < nums.length; j++){
if(nums[j] < nums[index]){
index = j;
}
}
if(index != i){
int temp = nums[index];
nums[index] = nums[i];
nums[i] = temp;
}
}
}
4.堆排序
4.1基本思想
排升序要建大堆,排降序建小堆。
4.2具体做法
- 先将初始序列K[1…n]建成一个大顶堆, 那么此时第一个元素K1最大, 此堆为初始的无序区.
- 再将关键字最大的记录K1 (即堆顶, 第一个元素)和无序区的最后一个记录 Kn 交换, 由此得到新的无序区K[1…n−1]和有序区K[n], 且满足K[1…n−1].keys⩽K[n].key
- 交换K1 和 Kn 后, 堆顶可能违反堆性质, 因此需将K[1…n−1]调整为堆. 然后重复步骤②, 直到无序区只有一个元素时停止.
4.3示例
4.4代码实现
public static void heapSort(int[] arr){
for(int i = arr.length; i > 0; i--){
max_heapify(arr, i);
//堆顶元素(第一个元素)与Kn交换
int temp = arr[0];
arr[0] = arr[i-1];
arr[i-1] = temp;
}
}
private static void max_heapify(int[] arr, int limit){
if(arr.length <= 0 || arr.length < limit)
return;
int parentIdx = limit / 2 - 1;
for(; parentIdx >= 0; parentIdx--){
if(parentIdx * 2 >= limit){
continue;
}
//左子节点位置
int left = parentIdx * 2 + 1;
//右子节点位置,如果没有右节点,默认为左节点位置
int right = (left + 1) >= limit ? left : (left + 1);
int maxChildId = arr[left] >= arr[right] ? left : right;
//交换父节点与左右子节点中的最大值
if(arr[maxChildId] > arr[parentIdx]){
int temp = arr[parentIdx];
arr[parentIdx] = arr[maxChildId];
arr[maxChildId] = temp;
}
}
System.out.println("Max_Heapify: " + Arrays.toString(arr));
}
5冒泡排序
5.1基本思想
在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。
5.2示例
5.3代码实现
public static void bubbleSort(int [] nums){
for(int i = 1; i < nums.length; i++){
for(int j = 1; j < nums.length-i+1; j++){
if(nums[j] < nums[j-1]){
int temp = nums[j];
nums[j] = nums[j-1];
nums[j-1] = temp;
}
}
}
}
6.快速排序
6.1基本思想
基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
6.2具体方法
- 选择一个基准元素,通常选择第一个元素或者最后一个元素,
- 通过一趟排序讲待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的 元素值比基准值大。
- 此时基准元素在其排好序后的正确位置
- 然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。
6.3示例
6.4代码实现
public static int pivot(int [] nums,int start, int end){
int temp = nums[start];
while(start < end){
while(start < end && nums[end] >= temp){
end--;
}
nums[start] = nums[end];
while(start < end && nums[start] <= temp){
start++;
}
nums[end] = nums[start];
}
nums[start] = temp;
return start;
}
public static void quick(int [] nums, int low, int high){
if(low < high){
int piv = pivot(nums,low,high);
quick(nums,low,piv - 1);
quick(nums,piv + 1,high);
}
}
7.归并排序
7.1基本思想
归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
7.2示例
7.3代码实现
public static void mergeSort(int [] array){
mergeSortInternal(array,0,array.length - 1);
}
public static void mergeSortInternal(int [] array,int low, int high){
if(low >= high){
return;
}
int mid = (low+high)/2;
mergeSortInternal(array,low,mid);
mergeSortInternal(array,mid+1,high);
merge(array,low,mid,high);
}
public static void merge(int [] array, int start,int mid,int end){
int s1 = start;
int e1 = mid;
int s2 = mid + 1;
int e2 = end;
int [] temp = new int [end-start+1];
int k = 0;
while(s1 <= e1 && s2 <= e2){
if(array[s1] < array[s2]){
temp[k++] = array[s1++];
}else{
temp[k++] = array[s2++];
}
}
while(s1 <= e1){
temp[k++] = array[s1++];
}
while(s2 <= e2){
temp[k++] = array[s2++];
}
for (int i = 0; i < temp.length; i++) {
array[i+start] = temp[i];
}
}
8.基数排序
8.1基本思想
基数排序是这样一种排序算法,我们可以从低位(个位)开始,根据个位数排序一次,然后根据十位数排序,再根据百位数进行排序……最终完成整个数组的排序。
对于十进制数字而言,每一位只会是 0~9 这十个数字,我们通常使用桶排序(计数排序)来完成每一位数的排序。桶排序是一种稳定的排序算法,基数排序的正确性依赖一种稳定的排序算法。
基数排序其实是分 LSD(从低位向高位排序) 和 MSD(从高位向低位排序) 两种。
8.2示例
8.3代码实现
public static void main(String[] args) {
int[] arr = new int[] { 321, 1234, 543, 324, 24, 960, 540, 672, 783, 1000 };
radixSort(arr);
printArray(arr);
}
public static void radixSort(int[] arr) {
int digit = getMaxDigit(arr); // 获取最大的数是多少位
for (int i = 0; i < digit; i++) {
bucketSort(arr, i); // 执行 digit 次 bucketSort 排序即可
}
}
public static int getMaxDigit(int[] arr) {
int digit = 1; // 默认只有一位
int base = 10; // 十进制每多一位,代表其值大了10倍
for (int i : arr) {
while (i > base) {
digit++;
base *= 10;
}
}
return digit;
}
public static void bucketSort(int[] arr, int digit) {
int base = (int) Math.pow(10, digit);
// init buckets
ArrayList<ArrayList<Integer>> buckets = new ArrayList<ArrayList<Integer>>();
for (int i = 0; i < 10; i++) { // 只有0~9这十个数,所以准备十个桶
buckets.add(new ArrayList<Integer>());
}
// sort
for (int i : arr) {
int index = i / base % 10;
buckets.get(index).add(i);
}
// output: copy back to arr
int index = 0;
for (ArrayList<Integer> bucket : buckets) {
for (int i : bucket) {
arr[index++] = i;
}
}
}
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}