- 冒泡排序bubbleSort
- 选择排序selectionSort
- 插入排序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