一.快速排序
快速排序的思想大体是从一堆数据中随机选取一个数据作为基准,把比它大的数据全部放在它的右边,把比它小的数据全部放在它左边,这样就可以确定选定数据的位置。让后通过递归分别处理选定数据的左右两边数据,重复以上操作即可完成排序。快排的时间复杂度一般为O(nlogn)到O(n^2)之间,因此不太稳定。
int move_element(vector<int>& v, int start, int end)
{
int rand_num = start + rand() % (end-start+1);//取数据中的一个随机数作为基准
swap(v[rand_num], v[0]);//将该数于第一个数做交换,便于后续处理
int j = 1;//设置一个数,代表小于基准数的数的序号
for (int i = 1; i <= end; i++)
{
if (v[i] < v[0])
{
swap(v[i], v[j]);//遍历数组,遇到小的就交换
j++;//更新下标
}
}
swap(v[j - 1], v[0]);//遍历完后,j-1后的数为大于等于基准数的数,与其交换就可以确定了基准数的最终位置
return j-1;//返回基准数的下标
}
void quick_sort(vector<int>& v,int start,int end)
{
//采用递归进行排序
if (start < end)
{
int index = move_element(v,start,end);//排序函数
quick_sort(v,index+1,end);//对左边数据进行排序
quick_sort(v,start,index-1);//对右边数据进行排序
}
}
def quick_sort(arr):
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quick_sort(left) + middle + quick_sort(right)
二.桶排序
桶排序是指将每个数据按照某种映射关系分配到若干桶中,然后再对桶内的元素进行排序,最后再将桶合并即可。一般要保证前一个桶中元素的最大值小于等于后一个桶中元素的最小值,这样方便合并。另外,对桶中数据的排序可以采用快速排序等高效的排序方法来节省时间。桶排序的时间复杂度一般与桶内排序的方法有关。数据越多,需要的桶就越多,消耗的内存也越多。桶排序一般不适合对大规模的数据进行排序。
void buckets_sort(vector<int>& v)
{
vector<vector<int>> buckets(v.size());//创建桶
for (int i = 0; i < v.size(); i++)
{
int index = v[i] / 10;//我这采用了简单的映射,一般采用更复杂的
buckets[index].push_back(v[i]);//数据入桶
}
for (int i = 0; i < buckets.size(); i++)
{
sort(buckets[i].begin(), buckets[i].end());//对桶中元素排序,可以采用更高效的排序算法
}
int index = 0;
for (int i = 0; i < buckets.size(); i++)
{
for (int j = 0; j < buckets[i].size(); j++)
{
v[index++] = buckets[i][j];//将桶中的元素放入原数组
}
}
}
def bucket_sort(arr, bsize=2):
if len(arr) <= 1:
return arr
minarr, maxarr = min(arr), max(arr)
bucket_count = (maxarr - minarr) // bsize + 1
buckets = [[] for _ in range(bucket_count)]
for num in arr:
index = (num - minarr) // bsize
buckets[index].append(num)
sorted_arr = []
for bucket in buckets:
sorted_arr.extend(sorted(bucket))
return sorted_arr
三.基数排序
基数排序是根据数字的十进制位进行排序,将数字的十进制位作为索引放入对应的队列中,然后再将元素按照队列顺序取出来,之后再对下一位的十进制位进行排序,重复上述操作即可完成排序。基数排序的时间复杂度为O(d(n+r)),d为数字位数,基数排序不需要进行比较,对于数位少的元素排序效率较高。
int getmax(vector<int> v)
{
int max = v[0];
for (int i = 0; i < v.size(); i++)
{
if (v[i] > max)
{
max = v[i];
}
}
return max;
}
void radix_sort(vector<int> &v)
{
int max = getmax(v);//获取数组中的最大值
for (int i = 1;max/i!=0; i*=10)//根据最大数的位数设置循环
{
vector<vector<int>> buckets(10);//创建桶存放数据
for (int j = 0; j < v.size(); j++)
{
buckets[(v[j] / i) % 10].push_back(v[j]);//根据数字的十进制位上的数入桶
}
int index = 0;
for (auto j : buckets)
{
for (int k = 0; k < j.size(); k++)
{
v[index++] = j[k];//将桶合并
}
}
}
}
def radix_sort(arr):
max_length = len(str(max(arr)))
for i in range(max_length):
buckets = [[] for _ in range(10)]
for number in arr:
digit = (number // (10 ** i)) % 10
buckets[digit].append(number)
arr = [num for bucket in buckets for num in bucket]
return arr
此法只能对0以及正整数进行排序,不能对负数以及浮点数进行排序。若要对其他类型的数进行排序,需要更改算法。比如要对全体整数进行排序的话,可以将桶数增加到十九个,前九个用于存放负数。
四.堆排序
堆排序是指利用数组来模拟堆。将数组类比为树状结构,再把这颗数堆化,即保证每个结点的数大于等于下面所有的数,然后再让树顶的元素与最后一位元素交换(最大元素排在最后),这样就确定了最大元素的位置。然后在对剩余的数字再次堆化(去除最后元素,因为已经排好顺序),再将树顶的元素与倒数第二个元素交换。重复以上操作即可完成排序。堆排序的时间复杂度为O(nlogn),效率高并且稳定。
void maxheapify(vector<int>& v, int n,int index)
{
int left = 2 * index;
while (left <= n)//比价结点的大小作交换
{
if (left + 1 <= n && v[left] > v[left - 1])
{
left++;
}
if (v[left-1] > v[index-1])
{
swap(v[left - 1], v[index - 1]);
index = left;
left *= 2;
}
else
{
break;
}
}
}
void heap_sort(vector<int> &v)
{
for (int i = v.size() / 2; i >= 1; i--)//堆化
{
maxheapify(v, v.size(), i);
}
for(int i = 0; i < v.size(); i++)
{
swap(v[0], v[v.size() - 1 - i]);//交换元素
maxheapify(v,v.size()-i-1,1);//堆化
}
}
def heapify(arr, n, i):
left=2*i
while left<=n:
if left + 1 <= n and arr[left] > arr[left - 1]:
left+=1
if arr[left-1] > arr[i-1]:
arr[left-1], arr[i-1] = arr[i-1], arr[left-1]
i = left
left *= 2
else:
break
def heapSort(arr):
n = len(arr)
for i in range(n // 2,0, -1):
heapify(arr, n, i)
for i in range(n):
arr[n-1-i], arr[0] = arr[0], arr[n-1-i] # 交换
heapify(arr, n-i-1, 1)
五.归并排序
归并排序是指通过递归将数据不断地折半拆分,然后再将拆分的数据排序,再通过递归两两组合,最后完成排序。归并排序是一种稳定的排序,并且算法的时间复杂度为O(logn)。
void merge(vector<int>& v, int l, int m, int r) {
int i = 0, j = 0, k = l;
int n1 = m - l + 1;
int n2 = r - m;
vector<int> L(n1), R(n2);
for (i = 0; i < n1; i++)
L[i] = v[l + i];
for (j = 0; j < n2; j++)
R[j] = v[m + 1 + j];
i = 0;
j = 0;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
v[k++] = L[i++];
}
else {
v[k++] = R[j++];
}
}
while (i < n1) {
v[k++] = L[i++];
}
while (j < n2) {
v[k++] = R[j++];
}
}
void mergeSort(vector<int>& v, int l, int r) {
if (l < r) {
int m = l + (r - l) / 2;
mergeSort(v, l, m);
mergeSort(v, m + 1, r);
merge(v, l, m, r);
}
}
def merge_sort(arr):
if len(arr) > 1:
mid = len(arr) // 2
L = arr[:mid]
R = arr[mid:]
merge_sort(L)
merge_sort(R)
i=j=k=0
while i < len(L) and j < len(R):
if L[i] < R[j]:
arr[k] = L[i]
i += 1
else:
arr[k] = R[j]
j += 1
k += 1
while i < len(L):
arr[k] = L[i]
i += 1
k += 1
while j < len(R):
arr[k] = R[j]
j += 1
k += 1
六.希尔排序
希尔排序是一种改进的插入排序。它的基本思想是将原始列表分成多个子列表,每个子列表的元素通过选择一个增量 gap 来确定,然后对这些子列表分别进行插入排序。随着算法的进行,gap 会逐渐减小,最终当 gap 为1时,整个列表将作为一个子列表进行最后一次插入排序。
希尔排序的关键在于通过这种方式,可以快速将一些较小的元素移动到列表的前端,较大的元素移动到后端,这样在执行最终的插入排序时,需要移动的元素数量较少,从而提高了排序的效率。
void shell_sort(vector<int>&v)
{
int n = v.size();
for (int gap = n / 2; gap > 0; gap /= 2) {
for (int i = gap; i < n; i++) {
int temp = v[i];
int j;
for (j = i; j >= gap && v[j - gap] > temp; j -= gap) {
v[j] = v[j - gap];
}
v[j] = temp;
}
}
}
def shell_sort(a):
gap=len(a)//2
while gap>0:
for i in range(gap,len(a)):
tmp=a[i]
j=i
while j>=gap and a[j-gap]>tmp:
a[j]=a[j-gap]
j-=gap
a[j]=tmp
gap=gap//2