内部排序(二)---基于散列表

#include<iostream>
#include<vector>
#include<algorithm>
#include<time.h>
#include"queue.h"
#define MIN -9999
#define MAX 9999
using namespace std;

// 基于散列表的排序
/*01 桶排序*/
int* find_extremum(int* arr, int n)     // 找数组中的极值(min,max)
{
	int* res = new int[2];    // res[0]:min    res[1]:max
	/*int min = MIN, max = MAX;*/
	int min = MAX, max = MIN;
	for (int i = 0; i < n; ++i)
	{
		if (arr[i] > max) max = arr[i];
		if (arr[i] < min) min = arr[i];
	}
	res[0] = min; res[1] = max;
	return res;
}
int* find_extremum(vector<int>& arr, int n)   // 函数重载
{
	int* res = new int[2];    // res[0]:min    res[1]:max
	int min = MAX, max = MIN;
	for (int i = 0; i < n; ++i)
	{
		if (arr[i] > max) max = arr[i];
		if (arr[i] < min) min = arr[i];
	}
	res[0] = min; res[1] = max;
	return res;
}

void bucket_sort(int* arr, int n)            // assert: 每个桶重复的元素不是很多
{
	int* min_max = find_extremum(arr, n);
	int min = min_max[0], max = min_max[1];
	int bucket_num = (max - min) / n + 1;    // 由于 是向下取整,最后要加上1
	vector<int>* bucket_array = new vector<int>[bucket_num];   // 每个桶装一个vector
	for (int i = 0; i < n; ++i)
	{
		bucket_array[(arr[i] - min) / n].push_back(arr[i]);
	}
	for (int i = 0; i < bucket_num; ++i) {
		if (bucket_array[i].empty()) continue;               // 空桶直接跳过
		sort(bucket_array[i].begin(), bucket_array[i].end());
	}
	int k = 0;
	for (int i = 0; i < bucket_num; ++i) {
		if (bucket_array[i].empty()) continue;               
		for (int j = 0; j < bucket_array[i].size(); ++j)
			arr[k++] = bucket_array[i][j];
	}
}

/*02 计数排序*/
void counting_sort(int* arr, int n)        // assert: 重复较多,元素范围相对集中
{
	int maxval = find_extremum(arr, n)[1];
	int* count_array = new int[maxval + 1];      // [0...maxval,maxval+1)
	memset(count_array, 0, (maxval+1) * sizeof(int));   // count_array 中每个元素初始化为 0
	for (int i = 0; i < n; ++i)
	{
		count_array[arr[i]]++;
	}
	// 再次遍历count_array得到最终结果
	int k = 0;                                  // 基于散列表排序recycle的老套路
	for (int i = 0; i < maxval+1; ++i)
	{
		while (count_array[i] > 0) {
			arr[k++] = i;
			count_array[i]--;
		}
	}
}
/*03 基数排序*/
queue<int> bucket_queue[10];     // 10个桶构成的数组,每个数组的元素是一个链队列
int get_digit(int* arr, int n)         // 最大的关键字有几位
{
	int max_num = find_extremum(arr, n)[1];
	int cnt = 0;
	while (max_num > 0)
	{
		max_num /= 10;
		cnt++;
	}
	return cnt;
}
void radix_sort(int* arr, int n)
{
	int digit = get_digit(arr, n);
	for (int i = 1; i <= digit; ++i)       // 外循环要做digit趟
	{
		// allocate
		for (int j = 0; j < n; ++j)       // 遍历数组
		{
			int index = 0; int digit_th = i;
			int tmp = arr[j];
			while (digit_th > 0)
			{
				index = tmp % 10;
				tmp /= 10;
				--digit_th;
			}
			bucket_queue[index].enqueue(arr[j]);
		}
		// recycle
		int k = 0; ptr_node<int> ptr = nullptr;     // 依然是老套路
		for (int l = 0; l < 10; ++l)                // 遍历桶
		{
			if (bucket_queue[l].empty()) continue;
			for (ptr = bucket_queue[l].first(); ptr != bucket_queue[l].tail; ptr = ptr->suc)
				arr[k++] = ptr->data;
			bucket_queue[l].clear();         // 清空桶
		}
	}
}
/*04 应用:最大间隙问题*/
/*题目描述: 在x轴上插入n个点  (x1,0) (x2,0) ...(xn,0) 求这n个点当中最大间隙  注:最边缘的
两个区间(-oo,xi] [xj,+oo)不算做间隙,即最多有n-1个间隙*/
int find_max_gap(int* points, int n)
{
	int* min_max = find_extremum(points, n);
	int min = min_max[0], max = min_max[1];
	if (min == max) return 0;                 // 退化情况
	vector<int>* bucket_array = new vector<int>[n];   // n个桶
	for (int i = 0; i < n; ++i)
	{
		int addr = (n - 1) * (points[i] - min) / (max - min);  // 每个gap.len=(max-min)/(n-1)  n个点,n-1个gap
		bucket_array[addr].push_back(points[i]);               // addr是基于此的简单地址映射
	}
	vector<vector<int>> bucket_arr_copy;    // 清除空桶
	for (int i = 0; i < n; ++i)
	{
		if (bucket_array[i].size() > 0) 
			bucket_arr_copy.push_back(bucket_array[i]);
	}
	int max_gap = 0;
	// 找出每个bucket当中最大最小值
	for (int i = 1,len=bucket_arr_copy.size(); i<len/*i < bucket_arr_copy.size()*/; ++i) // 细细品会
	{
		int* res1 = find_extremum(bucket_arr_copy[i - 1], bucket_arr_copy[i - 1].size());
		int min1 = res1[0], max1 = res1[1];
		int* res2= find_extremum(bucket_arr_copy[i], bucket_arr_copy[i].size());
		int min2 = res2[0], max2 = res2[1];
		if (min2 - max1 > max_gap) max_gap = min2 - max1;  // 推导见教材p279
	}
	return max_gap;
}
void show(int* arr, int len)
{
	for (int i = 0; i < len; ++i)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}
int main()
{
	srand((unsigned)time(nullptr));
	int arr[] = { 5,2,2,7,10,35,23,99,66,4,7};
	int len = sizeof(arr) / sizeof(int);
	/*01 bucket_sort*/
	/*bucket_sort(arr, len);
	cout << "after bucket_sort: ";
	show(arr, len);*/
	/*02 counting_sort*/
	/*counting_sort(arr, len);
	cout << "after counting_sort: ";
	show(arr, len);*/
	/*03 radix_sort*/
	/*radix_sort(arr, len);
	cout << "after radix_sort: ";
	show(arr, len);*/

	/*04 find_max_gap*/
	int points[10] = { 0 };
	for (int i = 0; i < 10; ++i) points[i] = (rand() % 200)-100;
	int num = sizeof(points) / sizeof(int);
	show(points, num);
	int max_gap = find_max_gap(points, num);
	cout << "max_gap = " << max_gap << endl;
	cout << "after sort: ";
	sort(points, points+num);
	show(points, num);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值