十大排序算法详解

目录

1. 冒泡排序

a. 思路

b. code

2. 插入排序

a. 思路

b. code

3. 希尔排序【插入排序plus】

a. 思路

b. code

4. 选择排序

a. 思路

b. code

5. 基数排序

a. 前置知识

b. 思路

c. code

6. 计数排序

a. 思路

b. code

7. 桶排序(计数排序plus & 基于分治)

a. 思路

b. code

8. 归并(基于分治)

a. 思路

b. code

9. 快排(基于分治)

a. 思路

b. code

10. 堆排序(以大顶堆为例)

a. 前置知识

b. 思路

c. code

11. 测试代码


1. 冒泡排序

a. 思路

  1. 外层循环控制次数,用来一次确定 len - 1, len - 2,len - 3, ... ,0 位置的数
  2. 内层循环遍历的过程中比较相邻两个数,将较大的数放置到数组右边

b. code

void bubble_sort(int arr[]) {
    for (int i = 0; i < len; i++) {
        for (int j = 0; j < len - i - 1; j ++) {
            if (arr[j + 1] < arr[j]) {
				swap(arr, j, j + 1);
            }
        }
    }
}
package bubble_sort;

public class Main {
    /**
     * @description: 冒泡排序
     * @param arr
     * @return void
     * @date: 2023/7/17 20:25
     */
    private static 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 + 1] < arr[j]) {
                    swap(arr, j, j + 1);
                }
            }
        }
    }
}

2. 插入排序

a. 思路

  1. 默认第一个数有序
  2. 从 index = 1 开始遍历数组,将第 i 个数倒序同之前的数进行比较并插入使得前 i + 1 项有序

b. code

void insert_sort(int arr[]) {
    for (int i = 1; i < len; i ++) {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j --;
        }
        arr[j + 1] = key;
    }
}
package insert_sort;

public class Main {
    /**
     * @description: 插入排序
     * @param arr
     * @return void
     * @date: 2023/7/17 23:21
     */
      private static void insertSort(int[] arr) {
        for (int i = 1; i < arr.length; i ++) {
            int key = arr[i];
            int j = i - 1;
            while (j >= 0 && arr[j] > key) {
                arr[j + 1] = arr[j];
                j --;
            }
            arr[j + 1] = key;
        }
    }
}

3. 希尔排序【插入排序plus】

a. 思路

  1. 设置循环增量 gap,迭代 gap /= 2
  2. 每次循环对 【i,i + gap,i + 2 * gap...】(i in [0,gap]进行插入排序

b. code

void shell_sort(int arr[]) {
    // 初始增量 len / 2, 每次循环后, 增量 /= 2
    for (int gap = len / 2; gap > 0; gap /= 2) {
        // 每次采用插入排序
        for (int i = gap, j; i < len; i ++) {
            int key = arr[i];
            for (j = i; j >= gap && key < arr[j - gap]; j -= gap) {
                arr[j] = arr[j - gap];
            }
            arr[j] = key;
        }
    }
}
package shell_sort;

public class Main {
    /**
     * @description: 希尔排序
     * @param arr
     * @return void
     * @date: 2023/7/24 2:20
     */
    private static void shellSort(int[] arr) {
        // 初始增量 len / 2, 每次循环后, 增量 /= 2
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            // 每次采用插入排序
            for (int i = gap, j; i < arr.length; i ++) {
                int key = arr[i];
                for (j = i; j >= gap && key < arr[j - gap]; j -= gap) {
                    arr[j] = arr[j - gap];
                }
                arr[j] = key;
            }
        }
    }
    
}

4. 选择排序

a. 思路

  1. 外层循环控制当前排序的位置
  2. 内层循环遍历 i + 1 ~ j,查找arr在 i ~ j 上的最小数的索引
  3. 然后将查找到的最小的数与 arr[i] 交换顺序

b. code

void select_sort(int arr[]) {
	for (int i = 0; i < len; i++) {
		int minIndex = i;
		for (int j = i + 1; j < len; j ++) {
			if (arr[j] < arr[minIndex])
				minIndex = j;
		}
		swap(arr, i, minIndex);
	}
}
package select_sort;

public class Main {
    /**
     * @description: 选择排序
     * @param arr
     * @return void
     * @date: 2023/7/20 16:17
     */
    private static void selectSort(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            int minIndex = i;
            for (int j = i + 1; j < arr.length; j ++) {
                if (arr[j] < arr[minIndex])
                    minIndex = j;
            }
            swap(arr, i, minIndex);
        }
    }
}

5. 基数排序

a. 前置知识

队列:Queue| ProcessOn免费在线作图,在线流程图,在线思维导图

b. 思路

  1. 依次按照数组中数的个位、十位……将数放置到不同的桶中(分类)
  2. 将桶中的数赋值回原数组,作为该轮排序后的顺序(收集)

【PS】 : 每次排序都是基于上一轮的排序后的顺序进行,从而确保顺序不会错乱

c. code

#include <queue>

// 获取个位数(index = 0), 十位数(index = 1)...上的数
int get_num(int num, int lastIndex);
// 获取最大数的长度, 作为数组的分类次数
int get_cnt(int arr[]);
// 基数排序 
void radix_sort(int arr[]);


void radix_sort(int arr[]) {
    // 1. 找最大值的长度, 确定循环轮次
    int cnt = get_cnt(arr);

    // 2. 初始化十个桶
    queue<int> q[10];

    // 3.依次按照个位、十位数进行排序
    for (int i = 0; i < cnt; i++) {
        // 3.1 分类过程: 将数按照位数放入相应的桶中
        for (int j = 0; j < len; j ++) {
            q[get_num(arr[j], i)].push(arr[j]);
        }
        // 3.2 收集过程: 将桶中的数依次放入原数组 arr 中
        for (int j = 0, index = 0; j < 10; j ++) {
            while (!q[j].empty()) {
                arr[index ++] = q[j].front();
                q[j].pop();
            }
        }
    }
}

int get_num(int num, int lastIndex) {
	int res = num;
	for (int i = 0; i < lastIndex; i ++) {
		res /= 10;
	}
	return res % 10;
}

int get_cnt(int arr[]) {
	int max = arr[0]; 
	for (int i = 1; i < len; i ++) {
		if (arr[i] > max) {
			max = arr[i];
		}
	}
	int cnt = 0; 
	while (max > 0) {
		max /= 10;
		cnt ++;
	}
    return cnt;
}
package radix_sort;

import java.util.LinkedList;

public class Main {
    /**
     * @description: 基数排序
     * @param arr
     * @return void
     * @date: 2023/7/22 9:56
     */
    private static void radixSort(int[] arr) {
        // 1. 找最大值的长度, 确定循环轮次
        int cnt = getCnt(arr);

        // 2. 初始化十个桶
        LinkedList<Integer>[] list = new LinkedList[10];
        for (int i = 0; i < list.length; i++) {
            list[i] = new LinkedList<>();
        }

        // 3.依次按照个位、十位数进行排序
        for (int i = 0; i < cnt; i++) {
            // 3.1 分类过程: 将数按照位数放入相应的桶中
            for (int j = 0; j < arr.length; j ++) {
                list[getNum(arr[j], i)].offer(arr[j]);
            }
            // 3.2 收集过程: 将桶中的数依次放入原数组 arr 中
            for (int j = 0, index = 0; j < list.length; j ++) {
                while (!list[j].isEmpty()) {
                    arr[index ++] = list[j].poll();
                }
            }
        }
    }
    
    // 获取个位数(index = 0), 十位数(index = 1)...上的数
    private static int getNum(Integer num, int lastIndex) {
        return num.toString().length() <= lastIndex ? 0 : num.toString().charAt(num.toString().length() - lastIndex - 1) - '0';
    }

    // 获取最大数的长度, 作为数组的分类次数
    private static int getCnt(int[] arr) {
        return (Arrays.stream(arr).max().getAsInt() + "").length();
    }
}

6. 计数排序

a. 思路

  1. 构建临时数组,对 arr 中的数进行次数统计(计数)
  2. 按照统计好的结果给 arr 赋值

b. code

void cnt_sort(int arr[]) {
    // 1. 获取最大值、最小值
    int max = arr[0], min = arr[0];
    for (int i = 1; i < len; i ++) {
    	if (arr[i] > max) {
    		max = arr[i];
		} 
		if (arr[i] < min) {
			min = arr[i];
		}
	}
    
    // 2. 创建临时数组
    int temp_arr[max - min + 1];
    for (int i = 0; i < max - min + 1; i ++) {
        temp_arr[i] = 0;
    }

    // 3. 对 arr 进行计数
    for (int i = 0; i < len; i ++) {
        temp_arr[arr[i] - min] ++;
    }

    // 4. 按照统计结果将数赋值给原始数组 arr
    for (int i = 0, index = 0; i < max - min + 1; i ++) {
        while (temp_arr[i] -- > 0) {
            arr[index ++] = i + min;
        }
    }
}
package count_sort;

import java.util.Arrays;

public class Main {
    /**
     * @description: 计数排序
     * @param arr
     * @return void
     * @date: 2023/7/23 19:28
     */
    private static void cntSort(int[] arr) {
        // 1. 获取最大值、最小值
        int max = Arrays.stream(arr).max().getAsInt();
        int min = Arrays.stream(arr).min().getAsInt();
        
        // 2. 创建临时数组
        int[] tempArr = new int[max - min + 1];

        // 3. 对 arr 进行计数
        for (int i = 0; i < arr.length; i ++) {
            tempArr[arr[i] - min] ++;
        }

        // 4. 按照统计结果将数赋值给原始数组 arr
        for (int i = 0, index = 0; i < tempArr.length; i ++) {
            while (tempArr[i] -- > 0) {
                arr[index++] = i + min;
            }
        }
    }

}

7. 桶排序(计数排序plus & 基于分治)

a. 思路

  1. 划分为多个范围相同的区间,每个区间自排序(递归 or 调用别的排序算法)
  2. 将区间合并

【PS】:计数排序可以看成每个桶只存储相同元素,而桶排序每个桶存储一定范围的元素

b. code

#include <vector>
#include<algorithm>

void bucket_sort(int arr[]){
    // 1. 获取最大值、最小值
    int max = arr[0], min = arr[0];
    for (int i = 1; i < len; i ++) {
    	if (arr[i] > max) {
    		max = arr[i];
		} 
		if (arr[i] < min) {
			min = arr[i];
		}
	}
    // 2. 计算桶的数量并初始化桶集合 
    int bucketCnt = (max - min) / len + 1;
    vector<vector<int>> bucketList(bucketCnt);

    // 3. 将每个元素放入桶
    for (int i = 0; i < len; i ++) {
    	bucketList[(arr[i] - min) / len].push_back(arr[i]);
	}

    // 4. 对每个桶进行排序
    for (int i = 0; i < bucketList.size(); i ++) {
    	sort(bucketList[i].begin(), bucketList[i].end());
	}

    // 5. 将桶中的元素赋值到原序列
    for (int i = 0, index = 0; i < bucketCnt; i ++) {
    	for (int j = 0; j < bucketList[i].size(); j ++) {
    		arr[index ++] = bucketList[i][j];
		}
	}
}
package bucket_sort;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    /**
     * @description: 桶排序
     * @param arr
     * @return void
     * @date: 2023/7/24 0:48
     */
    public static void bucketSort(int[] arr){
        // 1. 计算最大值、最小值
        int max = Arrays.stream(arr).max().getAsInt();
        int min = Arrays.stream(arr).min().getAsInt();

        // 2. 计算桶的数量并初始化桶
        int bucketCnt = (max - min) / arr.length + 1;
        List<ArrayList<Integer>> bucketList = new ArrayList<ArrayList<Integer>>(){{
            for(int i = 0; i < bucketCnt; i++){
                add(new ArrayList<>());
            }
        }};

        // 3. 将每个元素放入桶
        Arrays.stream(arr).forEach(i -> bucketList.get((i - min) / arr.length).add(i));

        // 4. 对每个桶进行排序
        bucketList.forEach(Collections::sort);

        // 5. 将桶中的元素赋值到原序列
        AtomicInteger index = new AtomicInteger();
        bucketList.forEach(bucket -> bucket.forEach(i -> arr[index.getAndIncrement()] = i));
    }
}

8. 归并(基于分治)

a. 思路

  1. 确定分界点:mid =(l + r)/ 2
  2. 递归排序 left & right
  3. 归并,合二为一

【PS】:先递归处理,后合二为一

b. code

// 归并用的合并辅助数组 
int temp_arr[N];

// 归并排序 
void merge_sort(int arr[], int l, int r) {
	if (l >= r) {
		return;
	}

    // 递归
	int mid = l + ((r - l) >> 1);
	merge_sort(arr, l, mid);
	merge_sort(arr, mid + 1, r);

    // 合并
	int index = l, i = l, j = mid + 1;
	while (i <= mid && j <= r) {
		temp_arr[index ++] = arr[i] < arr[j] ? arr[i ++] : arr[j ++];
	} 
	while (i <= mid) temp_arr[index ++] = arr[i ++];
	while (j <= r) temp_arr[index ++] = arr[j ++];

    // 从排好序的数从临时数组放入到原数组
	for (int temp_index = l; temp_index <= r; temp_index ++) {
		arr[temp_index] = temp_arr[temp_index];
	}
	 
}
package merge_sort;

public class Main {
    
    private static int[] tempArr = new int[MAX_LEN];

    /**
     * @description: 归并排序
     * @param arr
     * @param l 左边界
     * @param r 右边界
     * @return void
     * @date: 2023/6/6 10:57
     */
    private static void mergeSort(int[] arr, int l, int r) {
        if (l >= r) {
            return;
        }

        int mid = l + ((r - l) >> 1);

        mergeSort(arr, l, mid);
        mergeSort(arr, mid + 1, r);

        int index = l, i = l, j = mid + 1;
        while (i <= mid && j <= r) {
            tempArr[index ++] = arr[i] <= arr[j] ? arr[i ++] : arr[j ++];
        }

        while (i <= mid) tempArr[index ++] = arr[i ++];
        while (j <= r) tempArr[index ++] = arr[j ++];

        for (int tempIndex = l; tempIndex <= r; tempIndex ++) {
            arr[tempIndex] = tempArr[tempIndex];
        }
    }
}

9. 快排(基于分治)

a. 思路

  1. 确定基准点 x = arr[l] or arr[(l + r) / 2] or arr[r] or 随机)
  2. 调整区间为:左边 <= 基准 x,右边 >= 基准 x
  3. 递归处理左右两段

【PS】:先划分边界,后递归处理

b. code

void quick_sort(int arr[], int l, int r) { 
	if (l >= r) {
		return;
	}
	
	// i, j 指向 l, r 的两侧, 因为 while 那儿使用 ++i 这种格式 
	int x = arr[l], i = l - 1, j = r + 1;

	while (i < j) {
        // 找左右两侧需要需要交换位置的点
		while (arr[++ i] < x);
		while (arr[-- j] > x);
        
        // 交换位置
		if (i < j)  swap(arr, i, j);
	} 
	
	// 递归: 这儿按 j 来写, 避免出现边界问题 
	quick_sort(arr, l, j);
	quick_sort(arr, j + 1, r);
}
package quick_sort;

public class Main {
    
    /**
     * @description: 快排
     * @param arr 排序数组
     * @param l 左边界
     * @param r 右边界
     * @return void
     * @date: 2023/6/6 4:54
     */
    private static void quickSort(int[] arr, int l, int r) {
        if (l >= r) {
            return;
        }

        int x = arr[l], i = l - 1, j = r + 1;

        while (i < j) {
            while (arr[++ i] < x);
            while (arr[-- j] > x);

            if (i < j) swap(arr, i, j);
        }

        quickSort(arr, l, j);
        quickSort(arr, j + 1, r);

    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    
}

10. 堆排序(以大顶堆为例)

a. 前置知识

大顶堆:父节点 > 左孩子 && 父节点 > 右孩子

b. 思路

  1. 基于原数组初始化大顶堆
  2. 将大顶堆的堆首堆尾交换位置,从而使堆尾之后的子数组有序
  3. 将堆的大小 - 1(1:刚刚交换的堆顶数,位置已确定),重新维护新的大顶堆

c. code

void heap_sort(int arr[]) {
    // 1. 构建大顶堆(从第一个有孩子节点的地方开始维护)
    for (int i = len / 2 - 1; i >= 0; -- i) {
        heapify(arr, i, len);
    }

    // 2. 排序
    for (int i = len - 1; i >= 1; -- i) {
        // 把大顶堆的堆顶元素与最后一个元素交换
        swap(arr, 0, i);
        // 对打乱的堆进行调整,恢复堆的特性
        heapify(arr, 0, i);
    }
}

void heapify(int arr[], int oldParent, int i) {
    // 1. 初始化父子节点索引
    int newParent = oldParent;
    int leftChild = 2 * oldParent + 1;
    int rightChild = 2 * oldParent + 2;

    // 2. 判断构建好后父节点应该在的位置 newParent
    if (leftChild < i && arr[newParent] < arr[leftChild]) newParent = leftChild;
    if (rightChild < i && arr[newParent] < arr[rightChild]) newParent = rightChild;

    // 3. 父节点更新的情况
    if (newParent != oldParent) {
        // 3.1 更新数组, 
        swap(arr, newParent, oldParent);
        // 3.2 维护交换后的子堆
        heapify(arr, newParent, i);
    }
}
package heap_sort;

public class Main {
    /**
     * @description: 堆排序
     * @param arr
     * @return void
     * @date: 2023/7/24 1:29
     */
    private static void heapSort(int[] arr) {
        // 1. 构建大顶堆(从第一个有孩子节点的地方开始维护)
        for (int i = arr.length / 2 - 1; i >= 0; -- i) {
            heapify(arr, i, arr.length);
        }

        // 2. 排序
        for (int len = arr.length - 1; len >= 1; -- len) {
            // 把大顶堆的堆顶元素与最后一个元素交换
            swap(arr, 0, len);
            // 对打乱的堆进行调整,恢复堆的特性
            heapify(arr, 0, len);
        }
    }

    /**
     * @description: 维护大顶堆的性质
     * @param arr
     * @param oldParent 原父节点坐标
     * @param len 需要构建的堆数组的长度
     * @return void
     * @date: 2023/7/24 1:50
     */
    private static void heapify(int[] arr, int oldParent, int len) {
        // 1. 初始化父子节点索引
        int newParent = oldParent;
        int leftChild = 2 * oldParent + 1;
        int rightChild = 2 * oldParent + 2;

        // 2. 判断构建好后父节点应该在的位置 newParent
        if (leftChild < len && arr[newParent] < arr[leftChild]) newParent = leftChild;
        if (rightChild < len && arr[newParent] < arr[rightChild]) newParent = rightChild;

        // 3. 父节点更新的情况
        if (newParent != oldParent) {
            // 3.1 更新数组, 
            swap(arr, newParent, oldParent);
            // 3.2 维护交换后的子堆
            heapify(arr, newParent, len);
        }
    }
}

11. 测试代码

#include <iostream>
#include <cstdlib>

using namespace std;

const int N = 100;

// 排序数组 
int arr[N];
// 数组长度 
int len;

void swap(int arr[], int i, int j) {
	int temp = arr[i];
	arr[i] = arr[j];
	arr[j] = temp;
}

int main() {
	len = rand() % N;
	printf("arr: "); 
	for (int i = 0; i < len; i ++) {
		arr[i] = rand() % N;
		printf("%d\t", arr[i]);
	}
	printf("\n"); 
	
	// ------------排序方法-----------
	
	xxx_sort(arr, 0, len - 1);
	
	// ------------------------------
	
	printf("arr: "); 
	for (int i = 0; i < len; i ++) {
		printf("%d\t", arr[i]);
	}
	return 0;
}
import java.util.Arrays;

public class Main {
    private static int MAX_LEN = 1000;
    private static int MAX_VALUE = 1000;

    private static int[] tempArr = new int[MAX_LEN];


    public static void main(String[] args) {
        int[] arr = new int[(int) (Math.random() * MAX_LEN + 1)];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) (Math.random() * MAX_VALUE + 1);
        }
        System.out.println("arr: ");
        System.out.println(Arrays.toString(arr));

        // ------------排序方法-----------
        
        xxxSort(arr, 0, arr.length - 1);
        
        // ------------------------------
        
        System.out.println("arr: ");
        System.out.println(Arrays.toString(arr));

    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值