c++排序算法:冒泡,插入,选择+快排,堆,归并,希尔+计数,基数,桶

本文深入讲解了多种排序算法,包括冒泡排序、选择排序、插入排序、希尔排序、快速排序、堆排序、归并排序、计数排序、基数排序和桶排序。详细介绍了每种算法的实现原理和步骤,为读者提供了丰富的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

冒泡排序

/*
(无序区,有序区)。从无序区通过交换找出最大元素放到有序区前端。
选择排序思路:
1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
3. 针对所有的元素重复以上的步骤,除了最后一个。
4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
*/
// 冒泡排序
void BubbleSort(vector<int>& v) {
	int len = v.size();
	for (int i = 0; i < len - 1; ++i)
		for (int j = 0; j < len - 1 - i; ++j)
			if (v[j] > v[j + 1]) 
				swap(v[j], v[j + 1]);
}
// 冒泡排序(改进版)
void BubbleSort_orderly(vector<int>& v) {
	int len = v.size();
	bool orderly = false;
	for (int i = 0; i < len - 1 && !orderly; ++i) {
		orderly = true;
		for (int j = 0; j < len - 1 - i; ++j) {
			if (v[j] > v[j + 1]) { // 从小到大
			orderly = false; // 发生交换则仍非有序
			swap(v[j], v[j + 1]);
			}
		}
	}
}

选择排序

/*
(有序区,无序区)。在无序区里找一个最小的元素跟在有序区的后面。对数组:比较得多,换得少。
选择排序思路:
1. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
2. 从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾
3. 以此类推,直到所有元素均排序完毕
*/
// 选择排序
void SelectionSort(vector<int>& v) {
	int min, len = v.size();
	for (int i = 0; i < len - 1; ++i) {
		min = i;
		for (int j = i + 1; j < len; ++j) {
			if (v[j] < v[min]) { // 标记最小的
				min = j;
			}
		}
		if (i != min) // 交换到前面
			swap(v[i], v[min]);
	}
}

插入排序

i,j

start+diff,i

n,start

小小互换

+-diff

/*
(有序区,无序区)。把无序区的第一个元素插入到有序区的合适的位置。对数组:比较得少,换得多。
插入排序思路:
1. 从第一个元素开始,该元素可以认为已经被排序
2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5. 将新元素插入到该位置后
6. 重复步骤2~5
*/
// 插入排序
void InsertSort(vector<int>& v){
        int start=0;
        int diff=1;
	for (int i = start+diff; i < v.size(); i+=diff)	
		for(int j = i; j>start&&v[j]<v[j-diff]; j-=diff)
                        swap(v[j],v[j-diff]);
}

希尔排序

diff=n/3;diff>=1;diff/=3

start:0~diff-1

插入排序

 

// 希尔排序:每一轮按照事先决定的间隔进行插入排序,间隔会依次缩小,最后一次一定要是1。
const int INCRGAP = 3;
void shellsort(vector<int> &v){
    int n=v.size();
    for(int diff= n / INCRGAP;diff> 0;diff/= INCRGAP)//遍历所有增量大小
        for(int start = 0;start < diff;start++){
            /*对子序列进行插入排序,当增量为1时,对所有元素进行最后一次插入排序*/
           for (int i = start+diff; i < v.size(); i+=diff)	
		for(int j = i; j>start&&v[j]<v[j-diff]; j-=diff)
                        swap(v[j],v[j-diff]);

快速排序

data[left++]<=key<=data[right--]

while(left<right) if(left<right)

data[right--]=data[left]

data[left++]=data[right]

/*
(小数,基准元素,大数)。在区间中随机挑选一个元素作基准,将小于基准的元素放在基准之前,大于基准的元素放在基准之后,再分别对小数区与大数区进行排序。
快速排序思路:
1. 选取第一个数为基准
2. 将比基准小的数交换到前面,比基准大的数交换到后面
3. 对左右区间重复第二步,直到各区间只有一个数
*/
// ----------------------------------------------------
// 快速排序(递归)
void QuickSort(vector<int>& v, int low, int high) {
	if (low >= high)		
		return;
        int key = v[low];// 也可以写个取v[low],v[high],v[(low+high)>>1]中位数做基准
        /*另一种写法
        int first=low-1;
        int key=nums[high];
        for(int last=low;last<ri;++last){
            if(nums[last]<=key)
                swap(nums[last],nums[++first]);
        }
        swap(nums[high],nums[++first]);
        */
	int first = low;		
	int last = high;        
	while (first < last){
		while (first < last && v[last] >= key)
			last--;
		if (first < last)
			v[first++] = v[last];
		while (first < last && v[first] <= key)
			first++;
		if (first < last)
			v[last--] = v[first];
	}

	v[first] = key;
	QuickSort(v, low, first - 1);
	QuickSort(v, first + 1, high);
}

堆排序

son=dad<<1+1;

儿子和儿子比,儿子和爸比,互换儿爸接着比

n/2-1~0开始建堆

0~(n-1~1)开始取最大值置后

// 堆排序:(最大堆,有序区)。从堆顶把根卸出来放在有序区之前,再恢复堆。
void max_heapify(int arr[], int start, int end) {
	//建立父節點指標和子節點指標
	int dad = start;
	int son = dad * 2 + 1;
	while (son <= end) { //若子節點指標在範圍內才做比較
		if (son + 1 <= end && arr[son] < arr[son + 1]) //先比較兩個子節點大小,選擇最大的
			son++;
		if (arr[dad] > arr[son]) //如果父節點大於子節點代表調整完畢,直接跳出函數
			return;
		else { //否則交換父子內容再繼續子節點和孫節點比較
			swap(arr[dad], arr[son]);
			dad = son;
			son = dad * 2 + 1;
		}
	}
}
void heap_sort(vector<int>arr) {
	len=arr.size();
	//初始化,i從最後一個父節點開始調整
	for (int i = len / 2 - 1; i >= 0; i--)
		max_heapify(arr, i, len - 1);
	//先將第一個元素和已经排好的元素前一位做交換,再從新調整(刚调整的元素之前的元素),直到排序完畢
	for (int i = len - 1; i > 0; i--) {
		swap(arr[0], arr[i]);
		max_heapify(arr, 0, i - 1);
	}
}

归并排序

二分

递归后序

data[a,b]+data[b+1,c]->temp[ ]->data[a,c]

// 归并排序:把数据分为两段,从两段中逐个选最小的元素移入新数据段的末尾。可从上到下或从下到上进行。
/*****************
    迭代版
*****************/
//整數或浮點數皆可使用,若要使用物件(class)時必須設定"小於"(<)的運算子功能
template<typename T>
void merge_sort(T arr[], int len) {
	T* a = arr;
	T* b = new T[len];
	for (int seg = 1; seg < len; seg += seg) {
		for (int start = 0; start < len; start += seg + seg) {
			int low = start, mid = min(start + seg, len), high = min(start + seg + seg, len);
			int k = low;
			int start1 = low, end1 = mid;
			int start2 = mid, end2 = high;
			while (start1 < end1 && start2 < end2)
				b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
			while (start1 < end1)
				b[k++] = a[start1++];
			while (start2 < end2)
				b[k++] = a[start2++];
		}
		T* temp = a;
		a = b;
		b = temp;
	}
	if (a != arr) {
		for (int i = 0; i < len; i++)
			b[i] = a[i];
		b = a;
	}
	delete[] b;
}
/*****************
    递归版
*****************/
template<typename T>
void merge_sort_recursive(T arr[], T reg[], int start, int end) {
	if (start >= end)
		return;
	int len = end - start, mid = (len >> 1) + start;
	int start1 = start, end1 = mid;
	int start2 = mid + 1, end2 = end;
	merge_sort_recursive(arr, reg, start1, end1);
	merge_sort_recursive(arr, reg, start2, end2);
	int k = start;
	while (start1 <= end1 && start2 <= end2)
		reg[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
	while (start1 <= end1)
		reg[k++] = arr[start1++];
	while (start2 <= end2)
		reg[k++] = arr[start2++];
	for (k = start; k <= end; k++)
		arr[k] = reg[k];
}
//整數或浮點數皆可使用,若要使用物件(class)時必須設定"小於"(<)的運算子功能
template<typename T> 
void merge_sort(T arr[], const int len) {
	T *reg = new T[len];
	merge_sort_recursive(arr, reg, 0, len - 1);
	delete[] reg;
}

计数排序

temp(n)

count(max_element+1)

置零+计数+求和+逆序赋值

/*****************
计数排序:统计小于等于该元素值的元素的个数i,于是该元素就放在目标数组的索引i位(i≥0)。
计数排序基于一个假设,待排序数列的所有数均为整数,且出现在(0,k)的区间之内。
如果 k(待排数组的最大值) 过大则会引起较大的空间复杂度,一般是用来排序 0 到 100 之间的数字的最好的算法,但是它不适合按字母顺序排序人名。
计数排序不是比较排序,排序的速度快于任何比较排序算法。
时间复杂度为 O(n+k),空间复杂度为 O(n+k)
算法的步骤如下:
1. 找出待排序的数组中最大和最小的元素
2. 统计数组中每个值为 i 的元素出现的次数,存入数组 C 的第 i 项
3. 对所有的计数累加(从 C 中的第一个元素开始,每一项和前一项相加)
4. 反向填充目标数组:将每个元素 i 放在新数组的第 C[i] 项,每放一个元素就将 C[i] 减去 1
*****************/
// 计数排序
void CountSort(vector<int>& data){//对比基数排序
        int n=data.size();
        vector<int>tmp(n);
        int m=max_element(data.begin(),data.end());
        vector<int>count(m+1,0); //for(j = 0; j < 10; j++) count[j] = 0; 
        for(j = 0; j < n; j++)
            k = data[j],++count[k];        
        for(j = 1; j <= m; j++)
            count[j] = count[j - 1] + count[j]; 
        for(j = n - 1; j >= 0; j--) {
            k = data[j];
            tmp[--count[k]] = data[j];
        }
        data=temp;//for(j = 0; j < n; j++) data[j] = tmp[j];        
}

基数排序

最大位数

计数排序(0~9):temp(n)+count(9+1)

data[i]除1/10/100/...再模10 得0~9

// 基数排序:一种多关键字的排序算法,可用桶排序实现。
int maxbit(vector<int> &data) //辅助函数,求数据的最大位数{
    int maxData = max_element(data.begin(),date.end());
    int d = 1;
    while (maxData >= 10) 
        maxData /= 10,++d;
    return d;
}
void radixsort(vector<int> &data) //基数排序{
    int n=data.size();
    int d = maxbit(data);
    vector<int>tmp(n);
    vector<int>count(10);
    int i, j, k;
    int radix = 1;
    for(i = 1; i <= d; i++,radix = radix * 10) //进行d次排序
    {
        for(j = 0; j < 10; j++)
            count[j] = 0; 
        for(j = 0; j < n; j++)
            k = (data[j] / radix) % 10,++count[k];        
        for(j = 1; j < 10; j++)
            count[j] = count[j - 1] + count[j]; 
        for(j = n - 1; j >= 0; j--) {
            k = (data[j] / radix) % 10;
            tmp[--count[k]] = data[j];
        }
        for(j = 0; j < n; j++) //将临时数组的内容复制到data中
            data[j] = tmp[j];        
    }
}

桶排序

int bucketsNum = (max - min)/100 + 1;//桶数
vector<list<int>> buckets(bucketsNum);//数据结构
insert(buckets[(value-min)/k],value);//list有序插入

void insert(list<int>& bucket,int val){
    auto iter = bucket.begin();
    while(iter != bucket.end() && val >= *iter) ++iter; 
    bucket.insert(iter,val);
}

void BucketSort_1(vector<int>& arr)
{
    int len = arr.size();
    if(len <= 1)
        return;
    int min = min_element(arr.begin(),arr.end());
    int max = max_element(arr.begin(),arr.end());
    int k = 10;//k为数字之间的间隔
    //向上取整,例如[0,9]有10个数,(9 - 0)/k + 1 = 1;
    int bucketsNum = (max - min)/k + 1;
    vector<list<int>> buckets(bucketsNum);
    for(int i=0;i<len;++i)
    {
        int value = arr[i];
        //(value-min)/k就是在哪个桶里面
        insert(buckets[(value-min)/k],value);
    }
    int index = 0;
    for(int i=0;i<bucketsNum;++i)
    {
        if(buckets[i].size())
        {
            for(auto& value:buckets[i])
                arr[index++] = value;
        }
    }
}
int main()
{
    vector<int> A={-100,13,14,94,33,82,25,59,94,65,23,45,27,43,25,39,10,35,54,90,-200,58};
    for(auto value:A)
        cout<<value<<" ";
    cout<<endl;
    BucketSort_1(A);

    for(auto value:A)
        cout<<value<<" ";
    cout<<endl;

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值