【acwing】排序与二分查找

1.快速排序

在学习数据结构时我已经讲过快速排序了,就是用hoare的单多趟实现完成的,今天我们用算法玩一玩。 

给你N个数让你升序排列区间[l,r]的每一个数。

上次用hoare法写的时候是双指针,一个是begin从开始,一个end从末尾开始,给一个标准值 。begin收集比标准值小,end收集大的,找到了就交换。

//快速排序单趟实现
int PartSort1(int* a, int begin, int end)
{
	int left = begin;
	int right = end;
	int key = begin;
	int keyi = begin;
	if (begin >= end)//如果最多有一个就不用递归了
	{
		return;
	}
	while (left < right)
	{
		while (left < right && a[right] >= a[key])
		{
			right--;
		}

		while (left < right && a[left] <= a[key])
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[left], &a[keyi]);
	return left;

我觉得这个写法有点邋遢,换个新玩法。

快速实现: 

 我们要排序[l, r]上的数字,我们直接从[l-1,r+1]开始,直接先+1,这样就不用在考虑边界的情况了。

#include<iostream>
using namespace std;

const int N = 1e6 + 10;
int n;
int q[N];

void quick_sort(int q[], int l, int r)
{
    if (l >= r) return;

    int i = l - 1, j = r + 1, x = q[l+r>>1];  //q[l]也行

    while (i < j)
    {
        do i++;
        while (q[i] < x); //找到比标准值大的就跳出循环
        do j--; 
        while (q[j] > x);//找到比标准值小的的就跳出循环
        if (i < j) swap(q[i], q[j]); //交换这两个另类
    }
    quick_sort(q, l, j);
    quick_sort(q, j + 1, r);
}

int main()
{
    while (cin >> n)
    {
        for (int i = 0; i < n; i++)
        {
            cin >> q[i];
        }

        quick_sort(q, 0, n - 1);
        for (int i = 0; i < n; i++)
        {
            cout << q[i] << ' ';
        }
   }
   
    return 0;
}

上面的快速排序函数就是一个模板,如果用到快排,直接套用这个模板就轻而易举的搞定了。还等什么,直接背起来。

我们来解释一下这个代码:

x:基准值一般选择区间中间的那个数,比如区间6~10,他的基准值就是8=10000(二进制)>>1=1000。基准值也可以是q[l].无所谓。

我们从基准值分开,左边比它小,右边比他大。i是找左边比基准值大的数,j找右边比它小的数,找到就交换。

do while:就是让i此时指向的是比基准值小的数,循环继续,当它指向的是比基准值大的数时就停止,跳出循环。同理j也是这样。

两个递归作用;我们此时只是让基准值的左边小,右边大,但是左边和右边都是无序的所以我们用递归让左边的数继续重复上面的操作来排序,一直分割区间,直到有序,跳出递归。

其实两个形参也可以: 

#include<iostream>
using namespace std;

const int N = 1e6 + 10;
int n;
int q[N];

void quick_sort(int l, int r)
{
    if (l >= r) return;

    int i = l - 1, j = r + 1, x = q[l];

    while (i < j)
    {
        do i++;
        while (q[i] < x); //找到比标准值大的就跳出循环
        do j--;
        while (q[j] > x);//找到比标准值小的的就跳出循环
        if (i < j) swap(q[i], q[j]); //交换这两个另类
    }
    quick_sort(l, j);
    quick_sort( j + 1, r);
}

int main()
{
    while (cin >> n)
    {
        for (int i = 0; i < n; i++)
        {
            cin >> q[i];
        }

        quick_sort( 0, n - 1);
        for (int i = 0; i < n; i++)
        {
            cout << q[i] << ' ';
        }
    }
    return 0;
}

2 .归并排序

在归并排序中我们需要做三步:

  1. 在排序区间找中点出的下标,将区间分两半。(快排是找个数不是下标)
  2. 递归排序左右的区间(left,right)。
  3. 创建一个新数组,将上面的左右区间合二为一。
#include <iostream>
using namespace std;

const int N = 1e5 + 10;
int a[N], tmp[N];

void merge_sort(int q[], int l, int r)
{
    if (l >= r)
        return;
    int mid = l + r >> 1;
    merge_sort(q, l, mid), merge_sort(q, mid + 1, r);

    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r)
    {
        if (q[i] <= q[j]) 
            tmp[k++] = q[i++]; //把较小的q[i]收入其中
        else
            tmp[k++] = q[j++];
    }
    while (i <= mid ) //如果j已经合并完但是i还有剩余,就让i入数组。
    {
        tmp[k++] = q[i++];
    }
    while (j <= r)
    {
        tmp[k++] = q[j++];
    }
    for (i = l, j = 0; i <= r; i++, j++)  //将排序好的区间加入的原来的数中
    {
        q[i] = tmp[j];
    }
}

int main()
{
    int n;
    cin >> n;
    for (int i = 0; i < n; i++)
    {
       cin>>a[i];
    }
    merge_sort(a, 0, n - 1);
    for (int i = 0; i < n; i++)
    {
       cout<<a[i]<<" ";
    }
    return 0;
}

这个思想时先调用递归进行切割区间,把一个大区间切割成若干个小区间然后比较小区间中的数,比较完返回更大一级区间在比较直到把left,right两个区间全部变成升序的。

让后用一个空数组来装这些数,如果其中一个全部进入另一个还有剩余,那就让剩余的直接归并到数组中然后我们把这个升序的新数组付给区间[l,r]。要是不赋给它,那在N个数中让我们排序的区间没变。

 其实归并函数两个参数也可以,把q[N]设成全局变量。

#include <iostream>
using namespace std;

const int N = 1e5 + 10;
int q[N], tmp[N];

void merge_sort( int l, int r)
{
    if (l >= r)
        return;
    int mid = l + r >> 1;
    merge_sort( l, mid), merge_sort( mid + 1, r);

    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r)
    {
        if (q[i] <= q[j]) 
            tmp[k++] = q[i++]; //把较小的q[i]收入其中
        else
            tmp[k++] = q[j++];
    }
    while (i <= mid ) //如果j已经合并完但是i还有剩余,就让i入数组。
    {
        tmp[k++] = q[i++];
    }
    while (j <= r)
    {
        tmp[k++] = q[j++];
    }
    for (i = l, j = 0; i <= r; i++, j++)  //将排序好的区间加入的原来的数中
    {
        q[i] = tmp[j];
    }
}

int main()
{
    int n;
    cin >> n;
    for (int i = 0; i < n; i++)
    {
       cin>>q[i];
    }
    merge_sort( 0, n - 1);
    for (int i = 0; i < n; i++)
    {
       cout<<q[i]<<" ";
    }
    return 0;
}

接下来我看到有位大佬总结了其余5中种排序的模板,我给各位看一下。

  • 1.插入排序
void insert_sort()
{
    for (int i = 1; i < n; i ++ )
    {
        int x = a[i];
        int j = i-1;

        while (j >= 0 && x < a[j])
        {
            a[j+1] = a[j];
            j -- ;
        }
        a[j+1] = x;
    }
}
  • 2.选择排序:
void select_sort()
{
    for (int i = 0; i < n; i ++ )
    {
        int k = i;
        for (int j = i+1; j < n; j ++ )
        {
            if (a[j] < a[k])
                k = j;
        }
        swap(a[i], a[k]);
    }

}
  • 3.冒泡排序:
void bubble_sort()
{
    for (int i = n-1; i >= 1; i -- )
    {
        bool flag = true;
        for (int j = 1; j <= i; j ++ )
            if (a[j-1] > a[j])
            {
                swap(a[j-1], a[j]);
                flag = false;
            }
        if (flag) return;
    }
}
  • 3.希尔排序:
void shell_sort()
{
    for (int gap = n >> 1; gap; gap >>= 1)
    {
        for (int i = gap; i < n; i ++ )
        {
            int x = a[i];
            int j;
            for (j = i; j >= gap && a[j-gap] > x; j -= gap)
                a[j] = a[j-gap];
            a[j] = x;
        }
    }
}
  • 4.堆排序:
void down(int u)
{
    int t = u;
    if (u<<1 <= n && h[u<<1] < h[t]) t = u<<1;
    if ((u<<1|1) <= n && h[u<<1|1] < h[t]) t = u<<1|1;
    if (u != t)
    {
        swap(h[u], h[t]);
        down(t);
    }
}

int main()
{
    for (int i = 1; i <= n; i ++ ) cin >> h[i];
    for (int i = n/2; i; i -- ) down(i);
    while (true)
    {
        if (!n) break;
        cout << h[1] << ' ';
        h[1] = h[n];
        n -- ;
        down(1);
    }
    return 0;
}

  • 5.计数排序:
void counting_sort()
{
    int sorted[N];
    int maxv = a[0];
    for (int i = 1; i < n; i ++ )
        if (maxv < a[i])
            maxv = a[i];
    int count[maxv+1];
    for (int i = 0; i < n; i ++ ) count[a[i]] ++ ;
    for (int i = 1; i <= maxv; i ++ ) count[i] += count[i-1];
    for (int i = n-1; i >= 0; i -- )
    {
        sorted[count[a[i]]-1] = a[i];
        count[a[i]] -- ;
    }
    for (int i = 0; i < n; i ++ ) a[i] = sorted[i];
}

这些模板可以直接套用,而且不存在边界问题。因为这是好多大佬用了很多的方法,实验搞出来的。

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值