目录
1、堆排序
2、Top_K问题
3、完结散花
个人主页:秋风起,再归来~
数据结构与算法
个人格言:悟已往之不谏,知来者犹可追
克心守己,律己则安!
1、堆排序
对一个无序的数组,因为数组中的元素是连续的,那我们就可以将数组中的元素进行建堆排序!
假设我们要对一个数组中的元素进行降序,那我们就要先将其进行向下调整建小堆,再将堆顶元素与堆的最后一个元素交换,那么数组中最小的那个元素就到最后面去了,最后一个数有序后,那我们就不再把它看作堆的元素了,最后再在堆顶进行向下调整保证小堆不变。这是一趟,假设数组中有N个元素,那我们进行(N-1)趟就行了。
下面是图解:
代码:
//交换
void swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
//向上调整
void AdjustUp(int* a, int child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (a[child] < a[parent])//< 建小堆;> 建大堆
{
swap(&a[child], &a[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
//向下调整
void AdjustDown(int* a, int size, int parent)
{
//先假设做孩子小
int child = parent * 2 + 1;
while (child < size)//当孩子超过最后一个叶子时结束循环
{
if ((child + 1 < size) && a[child + 1] < a[child])//如果右孩子小于左孩子,右孩子与父亲比较
{
child++;
}
if (a[child] < a[parent])//建小堆,即小的当父亲
{
swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//堆排序
void HeapSort(int* a, int len)
{
//建堆算法一(从第一个孩子向上调整)
//这种写法的时间复杂度为n*logn
//for (int i = 1; i < len; i++)
//{
// AdjustUp(a, i);//从第一个孩子向上调整建堆
//}
//
//建堆算法二(从倒数第一个根向下调整)
//这种写法的时间复杂度为O(N)[更优]
for (int i = (len-1-1)/2; i >= 0; i--)
{
AdjustDown(a, len,i);//从倒数第一个根向下调整建堆
}
int end = len - 1;
while (end > 0)
{
swap(&a[0], &a[end]);//交换第一个数与最后一个数
end--;
AdjustDown(a, end, 0);
}
}
int main()
{
int a[] = { 1,2,4,5,6,8,9,10 };
int len = sizeof(a) / sizeof(a[0]);
HeapSort(a, len);
for (int i = 0; i < len; i++)
{
printf("%d ", a[i]);
}
return 0;
}
2、Top_K问题
Top_K问题:
Top_K问题就是在大量数据中找出最大或最小的前K个
我们可以很轻松的想到用将这些全部数据进行建堆,在进行K次pop,这样我们就很容易的就可以找到最大或最小的前K个。但这里有一个问题,就是存储这么的数据势必会用到大量的空间,那我们有没有其他方法,在用到极小的的空间内解决问题呢?
当然可以!
假设我们要找出最大的前K个~
1、我们先将全部数据的前K个建成小堆
2、再将后N-K个数据分别和堆顶数据比较,比堆顶数据大就将堆顶数据覆盖
3、然后再从堆顶向下调整保持小堆结构
4、将后N-K个数据比较完后,小堆里面的数据就是最大的前K个
我这里先随机生成10万个随机数并且这些随机数都小于10万,并将这些数据都写进我们的文本文件中!
void CreatRandomNumber()
{
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen fail!\n");
return;
}
srand((unsigned int)time(NULL));
int n = 100000;//随机生成10万个数
for (int i = 0; i < n; i++)
{
int x = (rand() + i) % 100000;//这些数的大小都小于10万(方便后面检测)
fprintf(pf, "%d\n", x);//将数据写入文件当中
}
fclose(pf);
}
void TestTopK()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen fail!\n");
return;
}
int k = 0;
printf("请输入您要的前K个数:");
scanf("%d", & k);
int* HeapTopK = (int*)malloc(sizeof(int) * k);
//在文件中读取K个数放到数组中
for (int i = 0; i < k; i++)
{
fscanf(pf, "%d", &HeapTopK[i]);
}
//将这K个数建小堆
for (int i = (k - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(HeapTopK, k, i);//向下调整建堆
}
int x = 0;
//在10万个数中找前K个数
while (fscanf(pf, "%d", &x)>0)
{
if (x > HeapTopK[0])
{
HeapTopK[0] = x;
AdjustDown(HeapTopK, k, 0);
}
}
HeapSort(HeapTopK, k);
//打印前k个数
for (int i = 0; i < k; i++)
{
printf("%d ", HeapTopK[i]);
}
free(HeapTopK);
HeapTopK = NULL;
fclose(pf);
}
int main()
{
//CreatRandomNumber();
TestTopK();
return 0;
}
为了检查结果是否准确,我在文件中随机将10个数的大小手动调整成大于十万!
让我们来看看结果!
3、完结散花
好了,这期的分享到这里就结束了~
如果这篇博客对你有帮助的话,可以用你们的小手指点一个免费的赞并收藏起来哟~
如果期待博主下期内容的话,可以点点关注,避免找不到我了呢~
我们下期不见不散~~