排序算法专题

  1. 冒泡排序bubbleSort
  2. 选择排序selectionSort
  3. 插入排序insertSort
    从待排序列中选择一个元素插入到有序序列的合适位置,并且其后的元素向后移动。适合待排序列基本有序,这样移动较少。
    归并排序mergeSort
    快速排序quickSort
    堆排序heapSort

冒泡排序:相邻元素两两比较,将通过比较得到的最大元素放在最后
选择排序:总是选最小的元素放在当前有序序列的末尾,选n-1次
插入排序:插入有序序列的合适位置使其仍有序,插n-1次
归并排序:一分为二,两边有序后再合并。具体为反复一分为二,到一个元素分不了了(一个元素有序),再向上合并merge。典型的分治思想三部曲,分解子问题,求解子问题,合并子问题。递归边界
快速排序:partition返回的枢轴在整体有序序列的下标,一次划分partition后左边元素都比以partion为下标的元素小,右边元素都比它大,再对partion两边元素求解直到区间长度为1有序。分治思想,不需要合并子问题。

1、冒泡排序

def bubbleSort(nums, size):
    for _ in range(size - 1):
        flag = True
        for i in range(size - 1 - i):  # 对range(size-1)的优化
            if nums[i] > nums[i+1]:
                nums[i], nums[i+1] = nums[i+1], nums[i]
                flag = False
        if flag:
            break

if __name__ == '__main__':
    import numpy as np
    max_len = 15
    nums = np.arange(0, max_len)

    repeats = 1000
    while repeats:
        np.random.shuffle(nums)
        bubbleSort(nums, max_len)
        # print(nums)
        assert (nums == np.arange(max_len)).all()
        repeats -= 1
/*
冒泡排序
相邻两个元素比较,交换 
*/

#include<iostream>
#include<algorithm>
using namespace std;

void bubbleSort(int A[], int n)
{
	for(int i=0; i<n-1; i++)
	{
		bool flag = true;
		for(int j=0; j<n-i-1; j++)	//j=0,第一趟比较n-1次 
		{
			if(A[j+1] < A[j])
			{
				swap(A[j], A[j+1]);
				flag = false;
			}
		}
		if(true == flag)
			break;
	}
}

int main()
{
	const int n = 5;
	int A[n] = {6,4,5,2,3};
	bubbleSort(A, n);
	for(int i=0; i<n; i++)
		cout<<A[i]<<" ";
		
	return 0;
}

2.选择排序

def selectSort(nums, size):
    for i in range(size - 1):  # 需要正确的填nums[i]
        min_idx, min_value = i, nums[i]
        for j in range(i+1, size):
            if nums[j] < min_value:
                min_idx, min_value = j, nums[j]
        if min_idx != i:
            nums[i], nums[min_idx] = nums[min_idx], nums[i]

if __name__ == '__main__':
    import numpy as np
    max_len = 15
    nums = np.arange(0, max_len)

    repeats = 1000
    while repeats:
        np.random.shuffle(nums)
        selectSort(nums, max_len)
        # print(nums)
        assert (nums == np.arange(max_len)).all()
        repeats -= 1
/*
选择排序,对数组 
排序函数形参为数组,返回类型为void,数组“传地址” 
*/

#include<iostream>
#include<algorithm>
using namespace std;

void selectionSort(int A[], int n)
{
	for(int i=0; i<n-1; i++)			//选当前最小的 元素放在A[i]处 
	{
		int idx = i;
		for(int j=i+1; j<n; j++)
		{
			if(A[j] < A[idx])
				idx = j;
		}
		
		if(idx != i)	//这个判断也可以去掉 
		{
			swap(A[idx], A[i]);
			//int temp = A[idx];
			//A[idx] = A[i];
			//A[i] = temp;
		}
	}
}

int main()
{
	int n = 5;
	int A[n] = {1,4,5,2,3};
	selectionSort(A, n);
	
	for(int i=0; i<n; i++)
		cout<<A[i]<<" ";
		
	return 0;
}

3、插入排序

注意:
第0个元素已经有序,只需要从第1个元素开始插入;
后移时会覆盖当前待插入的元素,需要提前记录
j的处理,找到有序序列中小于cur_num的元素位置j(可能没有则j=-1),则j+1即为cur_num的位置

def insertSort(nums, size):
    for i in range(1, size):
        cur_num = nums[i]
        j = i - 1
        while j >= 0:
            if nums[j] >= cur_num:
                nums[j+1] = nums[j]
            else:
                break
            j -= 1
        # for j in range(i-1, -1, -1):
        #     if nums[j] >= cur_num:
        #         nums[j+1] = nums[j]
        #     else:
        #         break
        #     # if j == 0:
        #     #     j -= 1
        nums[j+1] = cur_num

if __name__ == "__main__":
    import numpy as np

    max_len = 15
    nums = np.arange(0, max_len)

    repeats = 1000
    while repeats:
        np.random.shuffle(nums)
        insertSort(nums, max_len)
        # print(nums)
        assert (nums == np.arange(max_len)).all()
        repeats -= 1
/*
插入排序
只需要对后n-1个数插入到前面指定位置 
从后往前比较, 
*/

#include<iostream>
using namespace std;

void insertSort(int A[], int n)
{
	for(int i=1; i<n; i++)	//从第二个数(下标1)起都得插入 
	{
		int insertN = A[i];	//待插入的数,必须记录下来,不然后面往后挪动后会被覆盖 
		int j=i-1;
		for(; j>=0; j--)
		{
			if(insertN < A[j])
				A[j+1] = A[j];
			else
				break;
		}
		if(insertN != A[i]) 	//未向后挪动,即当前不改变就是有序序列 //这个判断也可以去掉																																							
			//A[j+1] = A[i];	错误,可能已经被覆盖,不再是原始序列的A[i]
			A[j+1] = insertN; 
	}
}
				 
int main()
{
	const int n = 5;
	int A[] = {1,4,5,2,3};
	insertSort(A, n);
	
	for(int i=0; i<n; i++)
		cout<<A[i]<<" ";
		
	return 0;
}

4、二路归并排序

注意:
range的右边界是开的,而right1、right2是闭的
只能mid、mid+1而不能mid-1、mid

def merge(nums, left1, right1, left2, right2):
    left1_copy, left2_copy = left1, left2
    nums_ans = [-1] * (right1 - left1 + 1 + right2 - left2 + 1)
    itr = 0
    while left1 <= right1 and left2 <= right2:
        if nums[left1] < nums[left2]:
            nums_ans[itr] = nums[left1]
            itr += 1
            left1 += 1
        else:
            nums_ans[itr] = nums[left2]
            itr += 1
            left2 += 1
    while left1 <= right1:
        nums_ans[itr] = nums[left1]
        itr += 1
        left1 += 1
    while left2 <= right2:
        nums_ans[itr] = nums[left2]
        itr += 1
        left2 += 1

    # 迁移到nums
    itr = 0
    for _ in range(left1_copy, right1 + 1):  # attention
        nums[left1_copy] = nums_ans[itr]
        left1_copy += 1
        itr += 1
    for _ in range(left2_copy, right2 + 1):
        nums[left2_copy] = nums_ans[itr]
        left2_copy += 1
        itr += 1


def mergeSort(nums, left, right):
    if left < right:
        mid = (left + right) // 2
        mergeSort(nums, left, mid)
        mergeSort(nums, mid + 1, right)
        merge(nums, left, mid, mid + 1, right)


if __name__ == '__main__':
    import numpy as np

    max_len = 15
    nums = np.arange(0, max_len)

    repeats = 1000
    while repeats:
        np.random.shuffle(nums)
        mergeSort(nums, 0, max_len-1)
        # print(nums)
        assert (nums == np.arange(max_len)).all()
        repeats -= 1
/*
归并排序
递归方式
*/

#include<iostream>
using namespace std;

void merge(int A[], int L1, int R1, int L2, int R2){
	int *B = new int(R1-L1+1+R2-L2+1);
	int k=0;
	int i=L1, j=L2;
	while(i<=R1 && j<=R2){
		if(A[i]<A[j])
			B[k++] = A[i++];
		else
			B[k++] = A[j++];
	}
	while(i<=R1)
		B[k++] = A[i++];
	while(j<=R2)
		B[k++] = A[j++];
		
	for(int i=0; i<k; i++)
		A[L1+i] = B[i];
}

void mergeSort(int A[], int left, int right){
	if(left<right){
		int mid = (left+right)/2;
		mergeSort(A, left, mid);
		mergeSort(A, mid+1, right);
		merge(A, left, mid, mid+1, right);
	}
}

int main(){
	//int A[5] = {5,3,5,6,8};
	int A[9] = {3,4,5,1,2,9,8,6,7};
	mergeSort(A,0,8);
	
	for(int i=0; i<9; i++)
		cout<<A[i]<<" ";
		
	return 0;
}	

5、快速排序

def partition(nums, left, right):
    pivot = nums[left]
    while left < right:
        while left < right and nums[right] >= pivot:
            right -= 1
        nums[left] = nums[right]
        # left += 1
        while left < right and nums[left] <= pivot:
            left += 1
        nums[right] = nums[left]
        # right -= 1
    nums[left] = pivot
    return left

def quickSort(nums, left, right):
    if left < right:
        pos = partition(nums, left, right)
        quickSort(nums, left, pos-1)
        quickSort(nums, pos+1, right)
    # return nums

if __name__ == '__main__':
    import numpy as np
    max_len = 15
    nums = np.arange(0, max_len)

    repeats = 1000
    while repeats:
        np.random.shuffle(nums)
        quickSort(nums, 0, max_len-1)
        # print(nums)
        assert (nums == np.arange(max_len)).all()
        repeats -= 1
#include<iostream>
using namespace std;

int partition(int A[], int left, int right){
	int pivot = A[left];						//记录枢轴 
	while(left<right){
		while(left<right && A[right]>=pivot)	//必须带等号,否则部分样例陷入死循环(该样例就是) 
			right--;
		A[left] = A[right]; // 不可以改为A[left++] = A[right];
		
		while(left<right && A[left]<=pivot)		//同上 
			left++;
		A[right] = A[left]; // 不可以改为A[right--] = A[left];
	} 
	//此时left==right
	A[left] = pivot;
	return left;
}

void quickSort(int A[], int left, int right){
	if(left<right){								
		int pos = partition(A, left, right);
		quickSort(A, left, pos-1);
		quickSort(A, pos+1, right);
	}
}

int main(){
	int A[5] = {5,3,5,6,8};
	quickSort(A,0,4);
	
	for(int i=0; i<5; i++)
		cout<<A[i]<<" ";
		
	return 0;
}	

6、堆排序

排序时每次替换第一个元素,末尾元素在不断前移(待排序列长度不断减小)
下浮时,要判断左右孩子是否存在,不存在时退出,存在一个时…存在两个时取最小者;判断最小者是否小于父结点,是的话才交换,否则退出

"""
待排序列:nums[1]-nums[seq_len] = nums[1:seq_len+1]
len(nums) = seq_len+1,nums[0]闲置
"""

def adjust_iteration(nums, k, seq_len):
    while 2 * k <= seq_len:
        if 2 * k + 1 <= seq_len and nums[2 * k + 1] < nums[2 * k]:
            i = 2 * k + 1
        else:
            i = 2 * k
        if nums[k] > nums[i]:
            nums[k], nums[i] = nums[i], nums[k]
            k = i
        else:
            break


def adjust_recursion(nums, k, seq_len):
    if 2 * k <= seq_len:
        if 2 * k + 1 <= seq_len and nums[2 * k + 1] < nums[2 * k]:
            i = 2 * k + 1
        else:
            i = 2 * k
        if nums[k] > nums[i]:
            nums[k], nums[i] = nums[i], nums[k]
            adjust_recursion(nums, i, seq_len)

def heapSort(nums, seq_len):
    # 第一步建堆:从最后一个非叶子结点调整
    for i in range(seq_len // 2, 0, -1):
        adjust_recursion(nums, i, seq_len)

    # 第二步反复取堆顶的最小元素,并用末尾元素替代,并重新调整
    for cur_len in range(seq_len, 1, -1):
        nums[1], nums[cur_len] = nums[cur_len], nums[1]
        adjust_recursion(nums, 1, cur_len-1)
    return nums

if __name__ == '__main__':
    import numpy as np
    np.random.seed(1)
    max_len = 15
    nums = np.arange(0, max_len)

    repeats = 1000
    while repeats:
        np.random.shuffle(nums)
        ans = heapSort(np.concatenate(([-1], nums)), max_len)
        # print(ans[1:])
        assert (ans[1:] == np.arange(max_len-1, -1, -1)).all()
        repeats -= 1
#include<iostream>
using namespace std;


void Adjust(int A[], int k, int len) {
	for(int i = 2*k; i<=len; i*=2) {
		if(i+1<=len && A[i+1]<A[i]) //找最小的孩子
			i++;
		if(A[i]<A[k]) {
			swap(A[i], A[k]); 
			k = i;		//此时A[k]替换到i的位置,可能破坏了以i下标结点为根的堆,
		}
	}
}
	 	
void heapSort(int A[], int len){	//下标为1-len
	//先构造初始堆,从最后一个非叶子结点起,不断调整
	for(int i=len/2; i>=1; i--)
		Adjust(A, i, len);
	//堆底元素替换堆顶元素,再以堆顶为根节点调整
	for(int i=len; i>1; i--){	//len-1次 
		swap(A[1],A[i]);
		Adjust(A,1,i-1); 		//剩下i-1个元素调整为对 
	}
} 

int main(){
	int A[6] = {0,5,3,5,6,8};
	heapSort(A,5);
	
	for(int i=1; i<6; i++)
		cout<<A[i]<<" ";
		
	return 0;
}		 

7、希尔排序

改进的插入排序基本组件是插入排序,同时引入了控制增量gap(最终一定得是1)
0、gap、2gap、
1、1+gap、2+gap、

gap = len // 2
while gap:
	for i in range(gap):
		排序  i、i+gap、
	gap  = gap // 2

8、桶排序

设置桶的数量5
找出待排序列元素的最大值maxValue、最小值minValue。
则每个桶的存放的数据范围大小为interval_len=(maxValue-minValue) // 5 + 1,并不是桶的容量,一个桶可以存着无数数据
第0桶存放数据的范围为minValue-minValue+interval_len只要(num-minValue) // interval_len==0 就放入该桶
第i桶存放数据的范围为minValue-minValue+interval_len只要(num-minValue) // interval_len==i 就放入该桶
因为桶间有序,故只需对非空的桶进行排序(需要某排序算法例如冒泡)

9、基数排序

时间复杂度O(kn),k为数位
空间复杂度O(k+n)
一种多关键字的排序算法,可用桶排序实现。

算法思想:
取得数组中的最大数,并取得位数;
arr为原始数组,从最低位开始取每个位组成radix数组;
对radix进行计数排序(利用计数排序适用于小范围数的特点)

10、计数排序

时间复杂度O(n+k),k是值域范围
空间复杂度O(k)

计数排序
计数排序统计小于等于该元素值的元素的个数i,于是该元素就放在目标数组的索引i位(i≥0)。

计数排序基于一个假设,待排序数列的所有数均为整数,且出现在(0,k)的区间之内。
如果 k(待排数组的最大值) 过大则会引起较大的空间复杂度,一般是用来排序 0 到 100 之间的数字的最好的算法,但是它不适合按字母顺序排序人名。
计数排序不是比较排序,排序的速度快于任何比较排序算法。

算法思想:
找出待排序的数组中最大和最小的元素;
统计数组中每个值为 i 的元素出现的次数,存入数组 C 的第 i 项;
对所有的计数累加(从 C 中的第一个元素开始,每一项和前一项相加);
向填充目标数组:将每个元素 i 放在新数组的第 C[i] 项,每放一个元素就将 C[i] 减去 1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值