定义
栈是一种常用的,重要的数据结构。是一种只允许在一端进行插入或删除操作的线性表。栈一重点是后进先出(LIFO结构)。栈与同线性表的逻辑结构相同,仍然是一对一的关系。栈按储存结构分为顺序栈和链栈,以顺序栈为主。
顺序栈
定义
顺序栈的定义可以分为静态分配和动态分配两种,两种分配定义如下
#define MAXSIZE 100
typedef struct {
int data[MAXSIZE];
int top;
}SqStack;//静态分配
静态分配包括一个data静态数组用于存放栈中元素,以及整型top充当栈顶指针(虽然是整型,但可以代表数组的下标,充当指针的左右)
#define MAXSIZE 100
typedef struct {
int *top;
int* base;
int stacksize;
}SqStack;//动态分配
动态分配包括两个整型指针top和base,top为栈顶指针,base为栈底指针,以及一个整型stacksize用来表示当前栈分配的存储空间,下面以动态分配来进行栈的基本操作。
初始化
由于栈的动态分配以两个指针为操作,base作为栈底指针,需要为其分配存储地址。
void InitStack(SqStack& S) {
S.base = (int*)malloc(MAXSIZE * sizeof(int));
S.top = S.base;
S.stacksize = MAXSIZE;
return;
}
判空
栈的判空用S.top==S.base来判断,当两个指针相等,则栈为空。
void CheckStackEmpty(SqStack S) {
if (S.top==S.base) {
printf("EMPTY");
return;
}
printf("NO EMPTY");
return;
}
清空栈
清空栈中元素,但保留栈,只需让栈顶指针指向栈底就可以。
int LengthStack(SqStack S) {
return S.top-S.base;
}
销毁栈
清空栈中元素且不保留栈,需释放栈底指针,并将stacksize值改变为0,两个指针指向NULL。
void DestoryStack(SqStack& S) {
free(S.base);
S.stacksize = 0;
S.top = NULL;
S.base = NULL;
return;
}
求栈长
int LengthStack(SqStack S) {
return S.top-S.base;
}
入栈
入栈操作首先要确定栈未满,在未满的情况下,把值赋给top当前所指,并让top上移
void Push(SqStack& S, int e) {
if (S.top-S.base>=MAXSIZE) {
printf("ERROR");
}
else
{
*S.top++ = e;
}
return;
}
出栈
出栈操作首先要确定栈未空,在未空的情况下,把top下移,并让此时所指元素出栈。(栈要下移,是因为top所指是栈顶元素上一位)
oid Pop(SqStack& S, int& e) {
if (S.top == S.base) {
printf("ERROR");
}
else {
e =*--S.top;
}
return;
}
链栈
定义
链栈是运算受限的单链表,只能在链表头部进行操作。定义如下:
#define MAXSIZE 100
typedef struct StackNode {
int data;
struct StackNode* next;
}StackNode,*LinkStack;
链表的头指针就是栈底,不需要头结点,链栈是不存在栈满的情况,空栈是头指针指向空。插入和删除操作仅在栈顶进行。
初始化
构造一个空栈,栈顶指针置为空
void InitLinkStack(LinkStack& S) {
S = NULL;
return;
}
判空
void CheckLinkStackEmpty(LinkStack& S) {
if (S == NULL)
printf("EMPTY");
else
printf("NO EMPTY");
return;
}
入栈
void PushLink(LinkStack& S, int e) {
StackNode* p = (LinkStack)malloc(sizeof(StackNode));
p->data = e;
p->next = S;
S = p;
return;
}
出栈
void PopLink(LinkStack& S, int &e) {
StackNode* p = (LinkStack)malloc(sizeof(StackNode));
e = S->data;
p = S;
S = S->next;
free(p);
return;
}
栈和递归
递归的定义
若一个对象部分地包含它自身,或用它自己给自己定义,则称这个对象是递归的;若一个过程直接地或间接地调用,则称这个过程为递归过程。如下面函数:
long Fact(long n) {
if (n == 0)return;
else return n * Fact(n - 1);
}
分治法
分治法就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。这个技巧是很多高效算法的基础。
分治法的设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
必备的三个条件:
1.能将一个问题转变为一个新问题,而新问题和原问题解法相同或类似,不同的仅是处理对象,且处理对象变化具有规律。
2.可以使问题得到简化。
3.必须有一个明确的递归出口(边界)。
分治法的时间复杂性
如果把一个大的复杂的问题分为a(a>1)个形式相同的子问题,这些子问题的规模为n/b,如果分解或者合并的复杂度为f(n),那么分治法可以表示为:
函数的调用
递归的优缺点
优点:代码更加简洁易读。
缺点:需要入栈和出栈操作,时间和空间消耗大。