八大排序算法1(冒泡 选择 插入 希尔 桶排序)

所有的都要烂熟于心,最好最差,平均。只有四个稳定,冒泡 插入 归并 基数 快排最好和平均都是O(NlogN),最差是O(nn),归并和堆排最好 最差 平均都是O(N*logN)
在这里插入图片描述

初级排序

冒泡排序

冒泡排序,从前往后就是把最大的冒泡到最后面,缩小区间,或者从最后面开始,每次把最小的冒泡到最前面

两种实现

打印数组的值

#include<stdio.h>
#include<assert.h>
void PrintAr(int* ar, int n)
{
    assert(ar != nullptr);
    for (int i = 0; i < n; ++i)
    {
        printf("%5d", ar[i]);
    }
    printf("\n");
}

交换函数swap

void swap(int* a, int* b) {
	assert(a!=nullptr&&b!=nullptr)
    int temp = *a;
    *a = *b;
    *b = temp;
}

这个是从后向前遍历,每次把最小的放到最前面
注意越界条件,j的取值,j+1 时候是否会越界

void bubblesort1(int* ar, int n) {
    assert(ar != nullptr);
    if (n <= 1)return;
    int i, j;
    for (int i = n - 1; i >= 0; i--) {
        for (int j = n - 2; j >= n - i - 1; j--) {
            if (ar[j] > ar[j + 1]) {
                swap(&ar[j], &ar[j + 1]);
            }
        }
    }
}

这个是从前往后遍历,每次把最大的放到最后面
注意越界条件,j的取值,j+1 时候会越界

void bubblesort2(int* ar, int n) {
    assert(ar != nullptr);
    if (n <= 1)return;
    int i, j;
    for (int i = 0; i < n; i++) {//控制趟数
        for (int j = 0; j < n - i-1; j++) {//控制范围
            if (ar[j] > ar[j + 1]) {
                swap(&ar[j], &ar[j + 1]);
            }
        }
    }

}

冒泡排序的优化算法

这个是从后向前遍历,每次把最小的放到最前面,注意越界条件,j的取值,j+1 时候是否会越界,假如排了几次,发现,已经有序了,就不用再继续排了,直接跳出就行

void bubblesort1plus(int* ar, int n) {
    assert(ar != nullptr);
    if (n <= 1)return;
    int i, j;
   
    for (int i = n - 1; i >= 0; i--) {
        bool flag = false;
        for (int j = n - 2; j >= n - i - 1; j--) {
            if (ar[j] > ar[j + 1]) {
                swap(&ar[j], &ar[j + 1]);
                flag = true;
            }
        }
        if (flag == false)break;
    }
}

选择排序

选择排序,就是每次在指定的区间选择最小的元素,放到最前面

编程要点

先判断指针是否为空,然后判断如果n<=1 则说明一个数据或者没有数据
则不用排序,直接返回,
外层循环只用从0开始遍历到n-2位置,因为最后一个数不用比较,一定是最大的,
如果i本身就是minpos,则不用交换

代码实现

void SelectSort(int* ar, int n)
{
    assert(ar != nullptr);
    if (n <= 1) return;
    for (int i = 0; i < n - 1; ++i)
    {
        int minpos = i;
        for (int j = i + 1; j < n; ++j)
        {
            if (ar[minpos] > ar[j])
            {
                minpos = j;
            }
        }
        if (i != minpos)
        {
            Swap(&ar[minpos], &ar[i]);
        }
    }
}

插入排序

插入排序,就是遍历每一个数据,将其插入到合适的位置,类似最大堆搜索函数的实现

编程要点

先判断指针是否为空,然后判断如果n<=1 则说明一个数据或者没有数据
//则不用排序,直接返回
//从第二个数据开始,看是否需要向前插入,
//如果大圩前面的数,则直接遍历到第三个数据
//如果小于前面一个数,则需要向前插入,
//插入步骤是,现将该数提取到temp,前面的数下标为j,将前面的数移动到该位置,即ar[j+1]=ar[j] 然后j–;
// 看temp是否能插入到前面的一个位置,如果该temp大于签的的数
// 则可以插入到j+1位置,否则,继续将j位置到j+1位置
//循环的结束条件是temp>ar[j] 和j==-1 则将temp放在j+1位置

代码实现

void InsertSort(int* ar, int n)
{
    assert(ar != nullptr);
    if (n <= 1) return;
    for (int i = 1; i < n; ++i)
    {
        PrintAr(ar, n);
        if (ar[i - 1] > ar[i])
        {
            int j = i - 1;
            int tmp = ar[j + 1];
            do
            {
                ar[j + 1] = ar[j];
                j = j - 1;
            } while (j >= 0 && ar[j] > tmp);
            ar[j + 1] = tmp;
        }
    }
}

进阶排序

希尔排序

基数(桶)排序

堆排序

注意从第一个非叶节点开始向上调整,非叶节点i=n/2-1;

分析

堆排序的思想就是先将待排序的序列建成大根堆,使得每个父节点的元素大于等于它的子节点。此时整个序列最大值即为堆顶 元素,我们将其与末尾元素交换,使末尾元素为最大值,然后再调整堆顶元素使得剩下的 n−1 个元素仍为大根堆,再重复执行以上操作我们即能得到一个有序的序列

#include<stdio.h>
#include<assert.h>
//打印数组的值
void PrintAr(int* ar, int n)
{
    assert(ar != nullptr);
    for (int i = 0; i < n; ++i)
    {
        printf("%5d", ar[i]);
    }
    printf("\n");
}
//向下搜索调整,start end都是下标
//实现的功能是将ar[start]放到合适的位置
void adjust(int* ar, int start, int end) {
    assert(ar != nullptr);
    int i = start;
    int j = 2 * i + 1;
    int temp = ar[i];
    while (j <= end) {
        if (j < end && ar[j] < ar[j + 1])j++;
        if (temp >= ar[j])break;
        ar[i] = ar[j];
        i = j;
        j = 2 * i + 1;
    }
    ar[i] = temp;
}
//交换数组中的两个元素
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}
//堆排序
//先是建立最大堆,就是从第一个非叶节点 下表为i=n/2-1  n为数组大小
//一直向上调整,
//然后每次将堆顶的最大值, ,每次需要
//重新将顶部调整一下,从0到i-1位置,最大元素不动
void heapsort(int* ar, int n) {
    assert(ar != nullptr);
    for (int i = n / 2 - 1; i >= 0;i--) {
        adjust(ar, i, n - 1);
    }
    for (int i = n - 1; i > 0;i--) {
        swap(&ar[0], &ar[i]);
        adjust(ar, 0, i-1);
    }
}
int main()
{
    int ar[] = { 23,56,90,78,67,100,12,45,34,89 };
    int n = sizeof(ar) / sizeof(ar[0]);
    heapsort(ar, n);
    PrintAr(ar, n);

    return 0;
}

用C++语法实现

//堆排序
void adjust(vector<int>& num, int start, int end) {
	int i = start;
	int j = 2 * i + 1;
	int tmp = num[start];
	while (j<=end) {
		if (j < end && num[j] < num[j + 1])j++;
		if (tmp >= num[j])break;
		num[i] = num[j];
		i = j;
		j = 2 * i + 1;
	}
	num[i] = tmp;
}
void swap(vector<int>& num, int start, int end) {
	int tmp = num[start];
	num[start] = num[end];
	num[end] = tmp;
}
void heapsort(vector<int>&num) {
	int n = num.size();
	for (int i = n / 2 - 1; i >= 0; i--) {
		adjust(num, i, n - 1);
	}

	for (int i = n-1; i >0; i--) {
		swap(num, 0, n - 1-i);
		adjust(num, 0, n - i - 2);
	}


}
//堆排9:35-55
int main() {
	vector<int>num{ 6,2,1,4,5,9,6,3,2,5,4,1,2 };
	int n = num.size();
	heapsort(num);
	for (auto x : num) {
		cout << x <<" ";
	}
	cout << endl;
	return 0;
}

归并排序

归并排序利用了分治的思想来对序列进行排序。对一个长为 n 的待排序的序列,我们将其分解成两个长度为n/2的子序列。每次先递归调用函数使两个子序列有序,然后我们再线性合并两个有序的子序列使整个序列有序

递归实现

void PrintAr(int* ar, int n)
{
	assert(ar != nullptr);
	for (int i = 0; i < n; ++i)
	{
		printf("%5d", ar[i]);
	}
	printf("\n");
}
//类似将两个有序的数组合并成一个有序的数组,
//一个数组,pos左边的有序,pos右边的也有序
void MergeAr(int* dest, int* src, int left, int pos, int right) {
	assert(dest != nullptr && src != nullptr);
	int i = left, j = pos + 1;
	int k = left;
	while (i <= pos && j <= right) {
		dest[k++] = src[i] < src[j] ? src[i++] : src[j++];
		
	}
	while (i <= pos) {
		dest[k++] = src[i++];
	}
	while (j<= right) {
		dest[k++] = src[j++];
	}
}
void CopyAr(int* dest, int* src, int left, int right) {
	assert(dest != nullptr && src != nullptr);
	for (int i = left; i <= right; i++) {
		dest[i] = src[i];
	}
}

//int m = left + (right - left) / 2;  不能是left + (right - left+1) / 2;
//每次到(0,1)  passsort(ar, br, 0, 1);  会陷入死递归,出不来栈溢出
//55  45  33  22  11  0
//0     1    2    3   4    5
// 
//m=2
//55  45  33    |         22  11  0
//0     1    2     |         3   4    5
//m=1
//55  45  |  33    |         22  11  0
//0     1   |   2     |         3   4    5
//m=0

//55 | 45  |  33    |         22  11  0
//0   |  1   |   2     |         3   4    5

//合并  使其有序
//55 | 45 
//0   |  1 
//45 | 55 
//0   |  1 

//下一次合并
//45 | 55  |  33   
//0   |  1   |   2   
//33 | 45  |  55   
//0   |  1   |   2   

//if (left < right) 不要写成  while(left<right)

//递归到只有一个数据的时候,退出,合并这两个数据,每一个数据自己是有序的,‘
//所以每次递归的底部是,一个数据,即left==right
void passsort(int* br, int* ar, int left, int right) {
	assert(ar != nullptr && br != nullptr);
	if (left < right) {
		int m = left + (right - left) / 2;
		passsort(br, ar, left, m);
		passsort(br, ar, m + 1, right);
		MergeAr(br, ar, left, m, right);
		CopyAr(ar, br, left, right);
		PrintAr(ar, 10);
	}
}
//归并排序
//开辟br存储归并后的ar
void mergesort(int* ar, int n) {
	assert (ar != nullptr);
	if (n <= 1)return;
	int* br = (int*)malloc(sizeof(int) * n);
	if (br == nullptr)exit(EXIT_FAILURE);
	passsort(br, ar, 0, n - 1);
	free(br);
	br = nullptr;
}
int main()
{
	int ar[] = { 78,12,23,90,100,34,89,45,56,67 };
	int n = sizeof(ar) / sizeof(ar[0]);
	PrintAr(ar, n);
	mergesort(ar, n);
	PrintAr(ar, n);
	return 0;
}

非递归实现 迭代实现 C++实现

//归并排序递归形式
void merge(vector<int>& nums, int l, int mid, int r) {
	int i = l, j = mid + 1;
	vector<int>tmp(r - l + 1, 0);
	int k = 0;
	while (i <= mid && j <= r) {
		if (nums[i] <= nums[j]) {
			tmp[k] = nums[i];
			i++;
		}
		else {
			tmp[k] = nums[j];
			j++;
		}
		k++;
	}
	while (i <= mid) {
		tmp[k++] = nums[i++];
	}
	while (j <= r) {
		tmp[k++] = nums[j++];
	}

	for (int i = 0, j = l; i < tmp.size(); i++, j++) {
		nums[j] = tmp[i];
	}
}
void mergesort1(vector<int>& nums, int l, int r) {
	if (l < r) {
		int mid = (l + r) / 2;
		mergesort1(nums, l, mid);
		mergesort1(nums, mid+1, r);
		merge(nums, l, mid, r);
	}
}
//归并排序的非递归形式
void nicepass(vector<int>& nums, int s, int n) {
	int i = 0;
	for (; i + 2 * s <= n - 1; i = i + 2 * s) {
		merge(nums, i, i + s - 1, i + 2 * s - 1);
	}
	if (i + s <= n - 1) {
		merge(nums, i, i + s - 1, n - 1);
	}
}
void mergesort2(vector<int>& nums,int n) {
	if (n <= 1)return;
	int s = 1;
	while (s < n) {
		nicepass(nums, s, n);
		s *= 2;
	}
}



int main() {
	vector<int>nums{6,2,3,5,1,2,3,6,9 };
	int n = nums.size();
	mergesort2(nums, n);
	for (auto& x : nums) {
		cout << x << " ";
	}
	return 0;
}

快速排序

快速排序的主要思想是通过划分将待排序的序列分成前后两部分,其中前一部分的数据都比后一部分的数据要小,然后再递归调用函数对两部分的序列分别进行快速排序,以此使整个序列达到有序。

递归实现

核心是分治算法的体现

算法要点

partitiaon函数算法要点
//将ar数组的数分成两部分,左边的部分是都是<=ar[i],右边的都是>ar[i]
//大致思路是,在右边开始遍历,找比temp小的数,找到后直接赋值给i下标
// 从左边遍历,找比temp大的数,找到后直接赋值给j下标
// while (i<j && ar[j]>temp)j–;中加条件i<j是因为,假如上一步i j就差一步
//j–后,i=j,就不用比较了,加入条件,if(i<j).在进行赋值,不然就是自己给自己赋值
//while (i < j && ar[i] <= temp)i++;,如果不加i<j,i++,j 和 i 就会错位
//导致i>j 就会出错

int Parition(int* ar, int left, int right) {
    assert(ar != nullptr);
    int i = left, j = right;
    int temp = ar[i];
    while (i < j) {//i==j就会退出
        while (i<j && ar[j]>temp)j--;
        if(i<j)ar[i] = ar[j];
        while (i < j && ar[i] <= temp)i++;
        if(i<j)ar[j] = ar[i];
    }
    ar[i] = temp;
    return i;
}

//不断地缩小区间,每次看pos,pos左边都小于等于ar[pos]
//右边都大于ar[pos] 就算是两个数,也要排序,所以,当left==right
//说明这一边只有一个数了,就不用再递归排序了

快排优化 三数取中

第一个数,最后一个数,中间的数,将中位数置换到数组开头。

//快排优化
void swap(vector<int>& num, int l, int r) {
	int tmp = num[l];
	num[l] = num[r];
	num[r] = tmp;
}
void setmid(vector<int>num, int l, int mid, int r) {
	if (num[mid] > num[r]) {
		swap(num[mid], num[r]);
	}
	if (num[l] < num[mid]) {
		swap(num[l], num[mid]);
	}
	if (num[l] > num[r]) {
		swap(num[l], num[r]);
	}

}
int parition2(vector<int>& num, int l, int r) {
	int i = l, j = r;
	setmid(num, l, (l + r) / 2, r);
	int tmp = num[l];
	while (i < j) {
		while (i<j && num[j]>tmp)j--;
		if (i < j)num[i] = num[j];
		while (i < j && num[i] <= tmp)i++;
		if (i < j)num[j] = num[i];
	}
	num[i] = tmp;
	return i;
}
代码实现

类似二叉树的前序遍历,二叉树是打印节点值,这里是调整一半,
结束条件:二叉树是该节点为空,快排是left==right 只有一个数

void Qsort(int* ar, int left, int right) {
    assert(ar != nullptr);
    if (left < right) {//如果left==right 只有一个数,就不用排序了
        int pos = Parition(ar, left, right);
        Qsort(ar, left, pos - 1);
        Qsort(ar, pos + 1, right);
    }   
}
void quicksort1(int* ar, int n) {
    assert(ar != nullptr);
    Qsort(ar, 0, n - 1);
}

非递归实现

编程要点

//非递归的排序算法 快排 需要用到队列,即链式队列,先进先出
//队列为空时说明排序完成,退出
//队列不为空时,就出队,将其当做一个区间,算出中间的pos,然后再将两个区间入队
//如果区间为pos + 1 == right即右边只有一个数据,就不要继续排序,不用入队,
//pos - 1 > left pos位置是,它左边都小于等于他自己,如果pos - 1 = left,左边只有一个数,不用入队

代码实现
#include<queue>
queue<int>qu;
void quicksort2(int* ar, int n) {
    assert(ar != nullptr);
    if (n <= 1)return;
    std::queue<int>qu;
    qu.push(0);
    qu.push(n - 1);
    int left, right;
    while (1) {

        if (qu.empty())break;
        left = qu.front();
        qu.pop();
        if (qu.empty())break;
        right = qu.front();
        qu.pop();
        int pos = Parition(ar, left, right);
        if (pos - 1 > left) {
            qu.push(left);
            qu.push(pos-1);
        }
        if (pos + 1 < right) {
            qu.push(pos+1);
            qu.push(right);
        }
    }
}

//67, 56, 90, 78, 44
//0 1 2 3 4
// 排序一次后
//44, 56, 67, 78, 90
//0 1 2 3 4

//队列的过程为
// 0 4
// 0 4 | 0 2 3 4
// 0 4 | 0 2 | 3 4 1 2
// 0 4 | 0 2 | 3 4 | 1 2
// 0 4 | 0 2 | 3 4 | 1 2 | 退出,排序完成

C++实现

//快排优化
void swap(vector<int>& num, int l, int r) {
	int tmp = num[l];
	num[l] = num[r];
	num[r] = tmp;
}
void setmid(vector<int>num, int l, int mid, int r) {
	if (num[mid] > num[r]) {
		swap(num[mid], num[r]);
	}
	if (num[l] < num[mid]) {
		swap(num[l], num[mid]);
	}
	if (num[l] > num[r]) {
		swap(num[l], num[r]);
	}

}
int parition2(vector<int>& num, int l, int r) {
	int i = l, j = r;
	setmid(num, l, (l + r) / 2, r);
	int tmp = num[l];
	while (i < j) {
		while (i<j && num[j]>tmp)j--;
		if (i < j)num[i] = num[j];
		while (i < j && num[i] <= tmp)i++;
		if (i < j)num[j] = num[i];
	}
	num[i] = tmp;
	return i;
}
int parition1(vector<int>&num, int l, int r) {
	int i = l, j = r;
	int tmp = num[i];
	while (i < j) {
		while (i<j && num[j]>tmp)j--;
		if (i < j)num[i] = num[j];
		while (i < j && num[i] <= tmp)i++;
		if (i < j)num[j] = num[i];
	}
	num[i] = tmp;
	return i;
}
void quicksort1(vector<int>& num, int l, int r) {
	if (l < r) {
		int mid = parition2(num, l, r);
		quicksort1(num, l, mid);
		quicksort1(num, mid + 1, r);
	}
}
void quicksort2(vector<int>&num, int l, int r) {
	std::queue<int>qu;
	qu.push(l);
	qu.push(r);
	while (1) {
		if (qu.empty())break;
		int left = qu.front(); qu.pop();
		if (qu.empty())break;
		int right = qu.front(); qu.pop();
		int mid = parition2(num, left, right);
		if (mid - 1 > left) {
			qu.push(left);
			qu.push(mid);
		}
		if (mid + 1 < right) {
			qu.push(mid + 1);
			qu.push(right);
		}
	}

}
int main() {
	vector<int>num{ 2,6,1,7,3 };
	int n = num.size();
	quicksort2(num, 0, n - 1);
	for (auto& x : num) {
		cout << x;
	}
	cout << endl;
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值