C 栈和堆
Posted 别碰我的宏定义
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C 栈和堆相关的知识,希望对你有一定的参考价值。
好久没写博客了,今天找了个时间,准备写一下栈和堆的知识。这些知识我也从各个方面整理和收集,希望对各位有用。废话不多说了,直接开整。
第一个问题面试高频考点:
请你说一下stackoverflow ,并简单举例说明,
栈溢出指的是程序向栈中写入某个变量时对内存进行了越界操作,从而导致栈中与其相邻的变量值被修改。
栈溢出的原因:1.局部数组过大,例如你定义了个int ar[60000],大约会导致栈溢出。
2.递归层数过多,导致栈溢出,例如斐波拉且数列,调用大约快100次的时候,它每次调用都会压栈,只压不释放到时候就会出现溢出。
3.指针或者数组指针越界,例如进行字符串拷贝,或者处理用户输入等等。
在这里插入代码片#include<stdio.h>
void func()
{
int fun[600000000000];
}
int main()
{
func();
return 0;
}
比如这个函数就是因为数组过大,而函数在被调过程中是基于会开辟栈帧,存放传入参数,返回值还有临时变量。而调用过大时会出现报错,编译不过。
#include<stdio.h>
int func(int i)
{
if (i == 1)
return 1;
return func(i - 1) + func(i - 2);
}
int main()
{
int i = 100;
func(i);
printf("100次递归");
return 0;
}
调用100次递归,程序奔溃。一直压栈,出栈导致栈溢出。导致stackoverflow
2.栈和堆的区别
堆是由低地址向高地址扩展,而栈是从高地址向低地址扩展。栈一般是由操作系统开辟和维护的,而堆是由程序员定义维护的,堆频繁调用malloc和free函数会导致产生内存碎片,降低程序效率,而栈是先进后出的这么一个结构,不会产生内存碎片。
计算机一般会给栈提供一些接口,分配专门的寄存器,而压栈入栈有专门的汇编指令,堆是由C/C++函数库提供的,机制复杂,且处理完之后还要转到汇编,所以栈比堆要快。
小根堆和大根堆:两者都是完全二叉树,两个要素,连续且缺失右节点。
好了,基础完成,开始搞代码,
我们用一个动态数组来模拟栈,它的特点是先进后出,就像是一群旅游去旅游,看见一个山洞,好奇心大发,想进去一探究竟,然后洞口只能每次一个人通过,等到最里面的人发现前面已经没路了,就需要外面的人先出去,他才能出去。只有等到它后面的最后一个人出来了,他才可以出来。
好了,定义一下今天需要实现的函数还有结构吧,先将上层架构搭好。
#ifndef _STACK_H_
#define _STACK_H_
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<assert.h>
#include<stdbool.h>
typedef char DateType;
typedef struct Stack{
int capacity; //数组容量
int size; //当前元素个数
DateType *array; //指向数组的指针
}Stack;
//栈的初始化
void StackInit(Stack *s,int capacity);
//压栈
void StackPush( Stack *s,DateType data);
//弹栈
void StackPop(Stack *s);
//显示栈顶元素
DateType StackTop(Stack *s);
//判栈满
int StackFill(Stack *s);
//扩容
void StackReserve(Stack *s);
//返回栈的大小
int StackSize(Stack *s);
//销毁栈`在这里插入代码片`
void StackDestroy(Stack *s);
//调试
void test1()
#endif
首先你需要有一个栈,故定义一个栈应该在所有操作之前。故功能还没实现先将需要实现的功能在测试函数理都调一边,需要测试那个,就将其他的屏掉,测试其他的就行了。
void test1()
{
Stack s;
StackInit(&s, 3);
StackPush(&s, 1);
StackPush(&s, 2);
StackPush(&s, 3);
StackPush(&s, 4);
StackPush(&s, 5);
StackPush(&s, 6);
printf("%d\\n",StackSize(&s));
StackPop(&s);
StackTop(&s);
StackPop(&s);
StackTop(&s);
StackPop(&s);
StackTop(&s);
printf("%d\\n", StackSize(&s));
StackDestroy(&s);
}
看着需要测试的功能将各个模块开始编写,先编写初始化函数将栈需要的空间开辟好。开辟大小应该为你初次需要的数组大小。将栈容量规定,堆元素个数置为0.
void StackInit(Stack *s,int capacity)
{
s->array = (DateType*)malloc(sizeof(Stack));
s->capacity = capacity;
s->size = 0;
}
初始化好之后,进行压入,相当于往数组里面塞元素。塞完后,元素个数加一。
void StackPush(Stack *s,DateType data)
{
if (StackFill(s))
StackReserve(s);
s->array[s->size] = data;
s->size++;
}
待到多次插入之后,如果栈插满,则需要将栈的大小扩大。数组盘满,如果元素个数和容量相同时,则栈满
int StackFill(Stack *s)
{
return s->size == s->capacity;
}
void StackReserve(Stack *s)
{
assert(s);
s->array = (DateType *)realloc(s->array, sizeof(DateType)*s->capacity * 2);
if (s->array == NULL)
{
assert(0);
}
s->capacity *= 2;
}
插入有了,删除的操作也不能少啊。删除就是栈顶指针下移,其中size 可以为栈元素的个数也可以为栈顶指针,可以说是多用了。
void StackPop(Stack *s)
{
assert(s);
if (s->size<=0)
printf("没有任何元素\\n");
s->size--;
}
压入,弹出实现了,可以调一个获取栈顶元素的函数来检查
//获取栈顶元素
DateType StackTop(Stack *s)
{
return s->array[s->size-1];
}
//返回栈的元素个数
int StackSize(Stack *s)
{
return s->size;
}
最后将动态申请的那个空间给他释放掉
void StackDestroy(Stack *s)
{
assert(s);
free(s->array);
s->array = NULL;
s->capacity = 0;
s->size = 0;
}
接下来实现本次博客的最后一个模块,包括堆排序和大根堆的建立
和前文的栈的手法一样,先搞上层架构,再来具体实现。
堆得大概模型
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int HPDateType;
typedef struct Heap
{
int *array;
int _size;
int _capacity;
}Heap;
//堆排序是在建立好大根堆或者小根堆的基础上进行的,大根堆拍完后是从小到达,小根堆拍完后是从大到小。具体是因为每次都把整个堆的最大(最小元素)放在最末尾,对其余元素接着排序实现的,
HeapSwap(Heap *hp,int *array, int k);
//最大堆的具体实现函数向下调整
void MaxHeapHelper(int *array, int size, int parent);
//最大堆的实现函数
void MaxHeapCreate(Heap *hp,int *array, int size);
// 堆的构建
void HeapCreate(Heap* hp, HPDateType* a, int n);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDateType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDateType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);
//增大堆的容量
void EnCreatHeap(Heap *hp);
void Print(Heap *hp);
接下来实现功能函数,堆的扩容,删除和获取堆顶元素,还有堆得元素和上面的栈配方一样。主要来一下那个堆排序,还有那个插入。
核心思想1:向下调整每次将左右根三个节点中的最大值放在根上,一次递归向下调整。
建立大堆的方法。
堆排序,由于大堆建立完成,堆顶元素为整个树的最大元素值,故每次将堆顶元素和队尾元素位置交换,将堆元素的个数减一,采用向下调整,直至排序成功。
#include"heap.h"
void MaxHeapHelper(int *array, int size, int parent)
{
int child = parent * 2 + 1;
while (child < size)
{
if (child + 1 < size && array[child + 1] > array[child])
child += 1;
if (array[child]>array[parent])
{
int temp = array[child];
array[child] = array[parent];
array[parent] = temp;
//进行循环向下调整,重点理解
MaxHeapHelper(array, size, child);
return;
}
else
{
return;
}
}
}
void MaxHeapCreate(Heap *hp,int *array, int size)
{
for (int root = (size - 1) / 2; root >= 0; root--)
{
MaxHeapHelper(array, size, root);
}
Print(hp);
}
void HeapSwap(Heap *hp,int *array, int k)
{
int temp = k;
while (temp)
{
int t = array[temp - 1];
array[temp - 1] = array[0];
array[0] = t;
temp--;
MaxHeapHelper(array, temp, 0);
}
Print(hp);
}
void Print(Heap *hp)
{
for (int i = 0; i < hp->_size; i++)
{
printf("%d ", hp->array[i]);
}
printf("\\n");
}
// 堆的构建
void HeapCreate(Heap *hp,HPDateType* a, int n)
{
hp->array = (HPDateType *)malloc(sizeof(HPDateType)*n);
hp->_capacity = n;
hp->_size = n;
for (int i = 0; i < n; i++)
{
hp->array[i] = a[i];
}
printf("建立大堆\\n");
MaxHeapCreate(hp,hp->array, hp->_capacity);
printf("堆排序,从小到大\\n");
HeapSwap(hp,hp->array,hp->_capacity);
printf("删除堆元素\\n");
HeapPop(hp);
Print(hp);
//HeapSwap(hp, hp->array, hp->_size);
printf("压入堆元素\\n");
HeapPush(hp, 1);
Print(hp);
MaxHeapCreate(hp, hp->array, hp->_capacity);
HeapSwap(hp, hp->array, hp->_size);
printf("堆顶元素 :%d\\n", HeapTop(hp));
printf("堆元素个数:%d\\n", HeapSize(hp));
}
删除元素的时候,先插到最后一个元素,然后给他向下调整,变为大根堆,删除则和排序操作类似。删除的话,将堆顶元素和最后一个元素交换,然后将最后一个元素删除,将堆调为大根堆。
//堆的扩容
void EnCreatHeap(Heap *hp)
{
assert(hp);
hp->array = (HPDateType *)realloc(hp->array, sizeof(HPDateType)*hp->_capacity * 2);
hp->_capacity *= 2;
}
// 堆的销毁
void HeapDestory(Heap* hp)
{
free(hp->array);
hp->_capacity = hp->_size = 0;
}
// 堆的插入
void HeapPush(Heap* hp, HPDateType x)
{
assert(hp);
if (hp->_size == hp->_capacity)
EnCreatHeap(hp);
hp->array[hp->_size++] = x;
}
// 堆的删除
void HeapPop(Heap* hp)
{
assert(hp);
if (!HeapEmpty(hp))
{
HPDateType temp = hp->array[hp->_size-1];
hp->array[hp->_size - 1] = hp->array[0];
hp->array[0] = temp;
hp->_size--;
}
}
// 取堆顶的数据
HPDateType HeapTop(Heap* hp)
{
return hp->array[0];
}
// 堆的数据个数
int HeapSize(Heap* hp)
{
assert(hp);
return hp->_size;
}
// 堆的判空
int HeapEmpty(Heap* hp)
{
assert(hp);
return hp->_size == 0;
}
#include"heap.h"
int main()
{
Heap hp;
HPDateType array[] = { 3, 2, 5, 8, 4, 7, 6, 9, 10 };
HeapCreate(&hp, array,9);
}
本次就到此为止吧,欢迎各位补充评论指点。
以上是关于C 栈和堆的主要内容,如果未能解决你的问题,请参考以下文章