【数据结构课程设计】基于商和余数的快速排序

#include<bits/stdc++.h>
typedef long long int ll;
//ceil() 向上取整
//sqrt()平方根
const int N = 11100;
using namespace std;

ll D[N] = { 0 };//待排序数组
ll MAX = 0, MIN = N;
ll m = 0;//待排序数组的变化范围
ll upintm = 0;//m开方向上取整
ll kw[N][N] = { 0 };//数组D的下标
int p, q;//p商,q余数
list <ll> link[N];//链接数组
ll n;//记录总个数	
bool veri_upintm;//判断输入的数组是否相同


void read_value(ll *D)//读入数据并计算最大值最小值
{
	int i = 1;//i从1开始变化,否则在链表赋值时无法区分第一个数重复和无重复的情况
	while (cin >> D[i])
	{
		if (D[i] == '#')//‘#’输入结束标志
			break;
		MAX = D[i] > MAX ? D[i] : MAX;
		MIN = D[i] < MIN ? D[i] : MIN;
		i++;
	}
	n = i - 1;//i在循环中多增一次,总数量比i少一
}
//求解m与upm
void calculate_m_upintm(ll &m, ll &upintm)
{
	m = MAX - MIN;
	upintm = ceil(sqrt(m));
}

//开辟upintm行upintm列的数组kw,一维链接数组link[n],并对kw和link数组进行初始化
void init_kw_link(ll kw[N][N], list <ll> *link)
{
	for (int i = 0; i <= upintm; i++)
	{
		for (int j = 0; j <= upintm; j++)
		{
			kw[i][j] = -1;//初始值为-1
		}
	}
	for (int i = 1; i <= n; i++)
	{
		link[i].push_back(-1);//初始值为-1
	}
}

bool verify_upintm()//判断数据是否全部相同
{
	if (upintm == 0)
		return false;
	return true;
}

void showD()//输出数组
{
	for (int i = 1; i <= n; i++)
		cout << D[i] << ' ';
}

void quick_sort(ll *D, ll kw[N][N], list <ll> *link)
{
	//计算每个D[i]与MIN的差对于upintm的商p和余数q
	for (int i = 1; i <= n; i++)
	{
		p = (D[i] - MIN) / upintm;
		q = (D[i] - MIN) % upintm;
		if (p != upintm)
		{
			//把D[i]的下标i放到kw的第p行第q列的位置,并赋值link[i]=0,否则链接起来
			if (kw[p][q] == -1)//如果还没有赋值,说明商p余q只有一个数字,把序号放在kw里
			{
				kw[p][q] = i;
				link[i].pop_back();//原来的-1出栈
				link[i].push_back(0);//0入栈
			}
			else//如果已经有值,则替换kw[p][q]位置的序号,将第i个link赋值原来kw的值
			{
				int temp = kw[p][q];
				kw[p][q] = i;
				link[i].pop_back();//原来的-1出栈
				link[i].push_back(temp);//被替换掉的kw的坐标入栈,用link记录,起索引作用
			}
		}
		else //如果p的值和平方m向上取整相等则跳过该数,根据计算,该数刚好是最大值
		{
			continue;
		}
	}
	
	int k = 1;//新数组的下标
	for (int p = 0; p <= upintm; p++)//此处ij从0开始,因为商和余数可以为0
	{
		for (int q = 0; q <= upintm; q++)
		{
			//从商0余0开始重新对D[]赋值,得到的新数组就是排序后的数组
			if (kw[p][q] == -1)//说明没有商p余q的输入,该位置为空,不需要复制转下一个位置
			{
				continue;
			}
			else if (kw[p][q] != -1 && link[kw[p][q]].back() == 0)//表示kw[i][j]只对应D[]中的一个关键字,即同商同余只有一个
			{
				D[k] = p * upintm + q + MIN;
				/*
				 对	p = (D[i] - MIN) / upintm;
					q = (D[i] - MIN) % upintm; 的还原*/
				k++;//第k位使用之后,开始填充下一个位置
			}
			else if (kw[p][q] != -1 && link[kw[p][q]].back() != 0)//表示D[]中有多个相同的关键字
			{
				//同商同余说明是相同的数字
				D[k] = p * upintm + q + MIN;
				k++;
				int h = link[kw[p][q]].back();//h用来存放link[i]中连接的上一个相同数的位置
				//将相同的数放入数组中,有多少个重复的就赋值多少次
				do
				{
					D[k] = p * upintm + q + MIN;
					k++;
					h = link[h].back();
				} while (h != 0);//循环跳出条件是link[i]中的数为0,即该数第一次出现的位置
			}
		}
	}
	int flag = 0;//记录是否有输出过数组D[N]
	while (1)
	{
		if (k > n)
		{
			showD();
			flag = 1;
		}
		else
		{
			D[k] = MAX;
			k++;
			/*在对kw操作时跳过了等于最大值的数,简化计算,直接把这些值放在最后即可
				即跳过了p==upintm的数
				m=MAX-MIN;
				upintm=√(m*m);
				MAX=MIN+upintm*upintm;
			*/
		}
		if (flag)//如果成功输出则跳出循环
			break;
	}
}

int main()
{
	system("color F0");//白底黑字
	cout << "请输入待排序的整数,每个数之间用空格分开,以‘#’作为输入结束标志:" << endl;
	read_value(D);//读入数组D[]
	cout << "共输入了" << n << "个数" << endl;
	calculate_m_upintm(m, upintm);//计算数据的差值m与m平方开放向上取整的upintm
	veri_upintm = verify_upintm();//判断upintm的合法性,是否可以作为被除数
	if (veri_upintm)//如果可以作为被除数,则进行排序操作
	{
		init_kw_link(kw, link);//对数组kw与链表数组link赋初值
		cout << "排序后:" << endl;
		quick_sort(D, kw, link);
	}
	else
		showD();//因为一组数据相同,可直接输出数组
	return 0;
}

/*
输入1:
23 4 21 28 23 4 10 15 26 25 15 23 8 10 9 28 17 3 24 3 20 14 26 13 18 25 7 19 26 23 #
输出1:
3 3 4 4 7 8 9 10 10 13 14 15 15 17 18 19 20 21 23 23 23 23 24 25 25 26 26 26 28 28

输入2:
-1 -1 -2 -3 -85 #
输出2:
-85 -3 -2 -1 -1

输入3:
10000 100000 10000000 1000 10 1 #
输出3:
1 10 1000 10000 100000 10000000

输入4:
5 5 5 5 5 #
输出4:
5 5 5 5 5

输入5:
584598 84569852 2962586 84126523 523 7426510 520 9520 8563 01749 #
输出5:
520 523 1749 8563 9520 584598 2962586 7426510 84126523 84569852

输入6:
-1 -85 -5623 -52 94865132 8465 5 #
输出6:
-5623 -85 -52 -1 5 8465 94865132
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值