栈和队列
栈(Stack):只允许在一端进行插入或删除操作的线性表。
栈顶(Top): 线性表允许进行插入和删除的那一端
栈底(Bottom): 固定的,不允许进行插入和删除操作的另一端。
空栈:不含任何元素的空表。
栈的一个明显的操作特性:后进先出(Last In First Out, LIFO),故又称为后进先出的线性表。
-
-
- 栈的基本操作
-
InitStack(&S):初始化一个空栈S。
StackEmpty(S):判断一个栈是否为空,若栈S为空返回true,否则返回false。
Push(&S,x):进栈,若栈S未满,将x加入使之成为新栈顶。
Pop(&S,&x)出栈,若栈S非空,弹出栈顶元素,并用x返回。
GetTop(S,&x):读栈顶元素,若栈S非空,用x返回栈顶元素。
ClearStack(&S):销毁栈,并释放栈S所占的存储空间。
在解答算法题时,若题干没有做出限制,可以直接使用这些基本的操作函数。
-
- 栈的顺序存储结构
- 顺序栈的实现
- 栈的顺序存储结构
栈的顺序存储称为顺序栈,它是利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时附设一个指针(top)指示当前栈顶的位置。
栈的顺序存储类型可描述为:
#define MaxSize 50 //定义栈中元素的最大个数
typedef struct {
ElemType data[MaxSize]; //存放栈中元素
int top; //栈顶指针
}SqStack;
栈顶指针:S.top,初始时设置S.top=-1;栈顶元素:S.data[S.top]。
进栈操作:栈不满时,栈顶指针先加1,再送值到栈顶元素。
出栈操作:栈非空时,先取栈顶元素值,再将栈顶指针减1。
栈空条件:S.top==-1;栈满条件:S.top==MaxSize-1;栈长:S.top+1。
-
-
- 顺序栈的基本运算
-
- 初始化
void InitStack(SqStack &S) {
S.top = -1; //初始化栈顶指针
}
- 判空栈
bool StackEmpty(SqStack &S) {
if (S.top == -1) //栈空
return true;
else //不空
return false;
}
- 进栈
bool Push(SqStack &S, ElemType x) {
if (S.top == MaxSize - 1) //栈满,报错
return false;
S.data[++S.top] = x; //指针先加1,再入栈
return true;
}
- 出栈
bool Pop(SqStack &S, ElemType &x) {
if (S.top == -1) //栈空,报错
return false;
x = S.data[S.top--]; //先出栈,指针再减1
return true;
}
- 读栈顶元素
bool GetTop(SqStack &S, ElemType &x) {
if (S.top == -1) //栈空,报错
return false;
x = S.data[S.top]; //x记录栈顶元素
return true;
}
注意:这里栈顶指针指向的就是栈顶元素,所以进栈时的操作是S.data[++S.top] = x;出栈时的操作时x = S.data[S.top--]。如果栈顶指针初始化为S.top=0,即栈顶指针指向栈顶元素的下一个位置,则入栈操作变为S.data[S.top++] = x;出栈操作变为x = S.data[--S.top]。相应的栈空、栈满条件也会发生变化。
-
-
- 共享栈
-
利用栈底位置相对不变的特性,可以让两个顺序栈共享一个一维数据空间,将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸。
两个栈的栈顶指针都指向栈顶元素,top0=-1时0号栈为空,top1=MaxSize时1号栈为空;仅当两个栈顶指针相邻(top1-top0=1)时,判断为栈满。当0号栈进栈时top0先加1再赋值,1号栈进栈时top1先减1再赋值;出栈时则刚好相反。
共享栈是为了更有效地利用存储空间,两个栈地空间相互调节,只有在整个存储空间被占满时才发生上溢。其存取数据的时间复杂度均为O(1),所以对存取效率没有什么影响。
采用链式存储的栈称为链栈,链栈的优点是便于多个栈共享存储空间和提高其效率,且不存在栈满上溢的情况。通常采用单链表实现,并规定所有操作都是在单链表的表头进行。这里规定链栈没有头结点,Lhead指向栈顶元素。
栈的链式存储类型可描述为:
typedef struct Linknode {
ElemType data; //数据域
struct Linknode *next; //指针域
}*LiStack; //栈类型定义
采用链式存储,便于结点的插入与删除。
-
- 习题
- 第一题
- 习题
假设以I和O分别表示入栈和出栈操作。栈的初态和终态均为空,入栈和出栈的操作序列可表示为仅由I和O组成的序列,可以操作的序列称为合法序列,否则称为非法序列。
①下面所示的序列中哪些是合法的?
A. IOIIOIOO B. IOOIOIIO C. IIIOIOIO D. IIIOOIOO
②通过对①的分析,写出一个算法,判定所给的操作序列是否合法。若合法,返回true,否则返回false(假定被判定的操作序列已存入一维数组中)。
- A和D是合法序列,B和C 是非法序列。
- 设被判定的操作序列已存入一维数组A中。
【算法思想】依次逐一扫描入栈出栈序列(即由”I”和”O”组成的字符串),每扫描至任一位置均需检查出栈次数(即”O”的个数)是否小于入栈次数(“I”的个数),若大于则为非法序列。扫描结束后,再判断入栈和出栈次数是否相等,若不相等则不合题意,为非法序列。
int Judge(char A[])
//判断字符数组A中的输入输出序列是否是合法序列。如是,返回true,否则返回false。
{
int i = 0; //i为下标。
int j = 0; int k = 0; //j和k分别为I和字母O的的个数。
while (A[i] != '\0') //未到字符数组尾。
{
switch (A[i])
{
case 'I': j++; break; //入栈次数增1
case 'O': k++;
if (k > j) { printf("序列非法\n"); exit(0); }
}
i++; //不论A[i]是'I'或'O',指针i均后移。
} //while
if (j != k) {
printf("序列非法\n");
return false;
}
else {
printf("序列合法\n");
return true;
}
}
-
-
- 第二题
-
设单链表的表头指针为h,结点结构由data和next两个域构成,其中data域为字符型。试设计算法判断该链表的前n个字符是否中心对称。例如xyx,xyyx都是中心对称。
【算法思想】使用栈来判断链表中的数据是否中心对称。将链表的前一半元素依次进栈。在处理链表的后一半元素时,当访问到链表的一个元素后,就从栈中弹出一个元素,两个元素比较,若相等,则将链表中下一个元素与栈中再弹出的元素比较,直至链表到尾。这时若是空栈,则得出链表中心对称的结论;否则,当链表中的一个元素与栈中弹出元素不等时,结论为链表非中心对称,结束算法的执行。
int dc(LinkList L, int n) {
//h是带头结点的n个元素单链表,本算法判断链表是否是中心对称
char s[n/2]; int i; //s字符栈
LNode *p = L->next; //p是链表的工作指针,指向待处理的当前元素
for(i = 0; i<n / 2; i++) { //链表前一半元素进栈
s[i] = p->data;
p = p->next;
}
i--; //恢复最后的i值
if (n % 2 == 1) //若n是奇数,后移过中心结点
p = p->next;
while (p != NULL && s[i] == p->data) { //检测是否中心对称
i--; //i充当找顶指针
p = p->next;
}
if (i == -1) //桟为空找
return 1; //链表中心对称
else
return 0; //链表不中心对称
}
注意:算法先将“链表的前一半”元素(字符)进栈。当n为偶数时,前一半和后一半的个数相同;当n为奇数时,链表中心结点字符不必比较,移动链表指针到下一字符开始比较。比较过程中遇到不相等时,立即退出while循环,不再进行比较。
-
-
- 第三题
-
设有两个栈s1,s2都釆用顺序栈方式,并且共享一个存储区[0, …, maxsize-1],为了尽量利用空间,减少溢出的可能,可釆用栈顶相向、迎面增长的存储方式。试设计s1,s2 有关入栈和出栈的操作算法。
两个栈共享向量空间,将两个栈的栈底设在向量两端,初始时,s1栈顶指针为-1,s2 栈顶指针为maxsize。两个栈顶指针相邻时为栈满。两个栈顶相向、迎面增长,栈顶指针指向栈顶元素。
#define maxsize 100 //两个栈共享顺序存储空间所能达到的最多元素数
#define elemtp int //假设元素类型为整型
typedef struct {
elemtp stack[maxsize]; //栈空间
int top[2]; //top为两个栈顶指针
}stk;
stk s; //s是如上定义的结构类型变量,为全局变量
本题的关键在于,两个桟入栈和退栈时的栈顶指针的计算。s1栈是通常意义下的栈;而 s2栈入栈操作时,其栈顶指针左移(减1),退栈时,栈顶指针右移(加1)。
此外,对于所有栈的操作,都要注意“入栈判满、出栈判空”的检查。
- 入栈操作
int push(int i, elemtp x) {
//入栈操作。i为栈号,i=0表示左边的s1栈,i=1表示右边的s2栈,x是入栈元素
//入栈成功返回1,否则返回0
if (i<0 || i>1) {
printf("栈号输入不对");
exit(0);
}
if (s.top[1] - s.top[0] == 1) {
printf("栈已满\n");
return 0;
}
switch (i) {
case 0: s.stack[++s.top[0]] = x; return 1; break;
case 1: s.stack[--s.top[1]] = x; return 1;
}
}
- 退栈操作
elemtp pop(int i) {
//退栈算法。i代表栈号,i=0时为s1栈,i=l时为s2栈
//退栈成功返回退栈元素,否则返回-1
if (i<0 || i>1) {
printf("栈号输入错误\n");
exit(0);
}
switch (i) {
case 0:
if (s.top[0] == -1) {
printf("栈空\n");
return -1;
}
else
return s.stack[s.top[0]--];
case 1:
if (s.top[1] == maxsize) {
printf("栈空\n");
return -1;
}
else
return s.stack[s.top[1]++];
}//switch
}
队列(Queue): 队列简称队,也是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除。向队列中插入元素称为入队或进队;删除元素称为出队或离队。这和我们日常生活中的排队是一致的,最早排队的也是最早离队的。其操作的特性是先进先出(First In First Out, FIFO),故又称为先进先出的线性表。
队头(Front):允许删除的一端,又称为队首。
队尾(Rear): 允许插入的一端。
空队列:不含任何元素的空表。
-
-
- 队列常见的基本操作
-
InitQueue(&Q): 初始化队列,构造一个空队列Q。
QueueEmpty(Q): 判队列空,若队列Q为空返回true,否则返回false。
EnQueue(&Q,x): 入队,若队列Q未满,将x加入,使之成为新的队尾。
DeQueue(&Q,&x): 出队,若队列Q非空,删除队头元素,并用x返回。
GetHead(Q,&x): 读队头元素,若队列Q非空,则将对头元素赋值给x。
需要注意的是,队列是操作受限的线性表,所以,不是任何对线性表的操作都可以作为队列的操作。比如,不可以随便读取队列中间的某个数据。
-
- 队列的顺序存储结构
- 队列的顺序存储
- 队列的顺序存储结构
队列的顺序实现是指分配一块连续的存储单元存放队列中的元素,并附设两个指针front和rear分别指示队头元素和队尾元素的位置。设队头指针指向队头元素,队尾指针指向队尾元素的下一个位置(也可以让rear指向队尾元素,front指向队头元素的前一个位置)。
队列的顺序存储类型可描述为:
#define MaxSize 50 //定义队列中元素的最大个数
typedef struct {
ElemType data[MaxSize]; //存放队列元素
int front, rear; //队头指针和队尾指针
}SqQueue;
初始状态(队空条件):Q.front == Q.rear == 0。
进队操作:队不满时,先送值到队尾元素,再将队尾指针加1。
出队操作:队不空时,先取队头元素值,再将队头指针加1。
但不能用Q.rear==MaxSize作为队列满的条件。
-
-
- 循环队列
-
将顺序队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上看成一个环,称为循环队列。当队首指针Q.front=MaxSize-1后,再前进一个位置就自动到0,这可以利用除法取余运算(%)来实现。
初始时: Q.front=Q.rear=0
队首指针进1: Q.front=(Q.front+1)%MaxSize
队尾指针进1: Q.rear=(Q.rear+1)%MaxSize
队列长度:(Q.rear+MaxSize-Q.front)%MaxSize
出队入队时:指针都按顺时针方向进1。
显然,对空的条件是Q.front==Q.rear。如果入队元素的速度快于出队元素的速度,队尾指针很快就赶上了队首指针,此时可以看出队满时也有Q.front==Q.rear。
为了区分队空还是队满的情况,有三种处理方式:
*(1)牺牲一个单元来区分队空和队满,入队时少用一个队列单元,这是一种较为普遍的做法。 约定以“队头指针在队尾指针的下一个位置作为队满的标志”。
队满条件为:(Q.rear+1)%MaxSize==Q.front。
队空条件仍为:Q.front==Q.rear。
队列中元素的个数:(Q.rear-Q.front+MaxSize)%MaxSize
(2)类型中增设表示元素个数的数据成员。这样,则队空的条件为Q.Size==0;队满的条件为Q.size==MaxSize。这两种情况都有Q.front==Q.rear。
(3)类型中增设tag数据成员,以区分是队满还是队空。tag等于0的情况下,若因删除导致Q.front==Q.rear则为空队;tag等于1的情况下,若因插入导致Q.front==Q.rear则为队满。
-
-
- 循环队列的操作
-
- 初始化
void InitQueue(SqQueue &Q) {
Q.rear = Q.front = 0; //初始化队首、队尾指针
}
- 判队空
bool IsEmpty(SqQueue Q) {
if (Q.rear == Q.front) return true; //队空条件
else return false;
}
- 入队
bool EnQueue(SqQueue &Q, ElemType x) {
if ((Q.rear + 1) % MaxSize == Q.front) return false; //队满
Q.data[Q.rear] = x;
Q.rear = (Q.rear + 1) % MaxSize; //队尾指针加1取模
return true;
}
- 出队
bool DeQueue(SqQueue &Q, ElemType &x) {
if (Q.rear == Q.front) return false; //队空,报错
x = Q.data[Q.front];
Q.front = (Q.front + 1) % MaxSize; //队头指针加1取模
return true;
}
-
- 队列的链式存储结构
- 队列的链式存储
- 队列的链式存储结构
队列的链式表示称为链队列,它实际上是一个同时带有队头指针和队尾指针的单链表。 头指针指向队头结点,尾指针指向队尾结点,即单链表的最后一个结点(注意与顺序表不同)。
队列的链式存储类型可描述为:
typedef struct { //链式队列结点
ElemType data;
struct LinkNode *next;
}LinkNode;
typedef struct { //链式队列
LinkNode *front, *rear; //队列的队头和队尾指针
}LinkQueue;
当Q.front==NULL且Q.rear==NULL时,链式队列为空。
出队时,首先判断队是否为空,若不空,则取出队头元素,将其从链表中摘除,并让Q.front指向下一个结点(若该结点为最后一个结点,则置Q.front和Q.rear都为NULL)。 入队时,建立一个新结点,将新结点插入到链表的尾部,并改让Q.rear指向这个新插入的结点(若原队列为空队,则令Q.front也指向该结点)。
不难看出,不设头结点的链式队列在操作上往往比较麻烦,因此,通常将链式队列设计成一个带头结点的单链表,这样插入和删除操作就统一了。
用单链表表示的链式队列特别适合于数据元素变化比较大的情形,而且不存在队列满且产生溢出的问题。另外,加入程序中要使用多个队列,与多个栈的情形一样,最好使用链式队列,这样就不会出现存储分配不合理和“溢出”的问题。
-
-
- 链式队列的基本操作
-
- 初始化
void InitQueue(LinkQueue &Q) {
Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode)); //建立头结点
Q.front->next = NULL; //初始为空
}
- 判空队
bool IsEmpty(LinkQueue Q) {
if (Q.front == Q.rear) return true;
else return false;
}
- 入队
void EnQueue(LinkQueue &Q, ElemType x) {
LinkNode *s = (LinkNode*)malloc(sizeof(LinkNode));
s->data = x; s->next = NULL; //创建新结点,插入到链尾
Q.rear->next = s;
Q.rear = s;
}
- 出队
bool DeQueue(LinkQueue &Q, ElemType &x) {
if (Q.front == Q.rear) return false; //空队
LinkNode *p = Q.front->next;
x = p->data;
Q.front->next = p->next;
if (Q.rear == p)
Q.rear = Q.front; //若原队列中只有一个结点,删除后变空
free(p);
return true;
}
-
- 习题
- 第一题
- 习题
如果希望循环队列中的元素都能得到利用,则需设置一个标识域tag,并以tag的值为0或1来区分队头指针front和队尾指针rear相同时的队列状态是“空”还是“满”。试编写与此结构相应的入队和出队算法。
在循环队列的类型结构中,增设一个tag的整型变量,进队时置tag为1,出队时置tag为0(因为只有入队操作可能导致队满,也只有出队操作可能导致队空)。队列Q初始时,置tag=0,front=rear=0。这样的队列的4要素如下:
队空条件:Q.front==Q.rear且Q.tag==0。
队满条件:Q.front==Q.rear且Q.tag==1。
进队操作:Q.data[Q.rear]=x; Q.rear=(Q.rear+1)%MaxSize; Q.tag=1。
出队操作:x=Q.data[Q.front]; Q.front=(Q.front+1)%MaxSize; Q.tag=0。
- 设”tag”法的循环队列入队算法:
int EnQueue1(SqQueue &Q, ElemType x) {
if (Q.front == Q.rear&&Q.tag == 1)
return 0; //两个条件都满足时则队满
Q.data[Q.rear] = x;
Q.rear = (Q.rear + 1) % MaxSize;
Q.tag = 1; //可能队满
return 1;
}
- 设”tag”法的循环队列出队算法:
int DeQueue1(SqQueue &Q, ElemType &x) {
if (Q.front == Q.rear&&Q.tag == 0)
return 0; //两个条件都满足时则队满
x = Q.data[Q.front];
Q.front = (Q.front + 1) % MaxSize;
Q.tag = 0; //可能队空
return 1;
}
-
-
- 第二题
-
Q是一个队列,S是一个空栈,实现将队列中的元素逆置的算法。
【算法思想】将队列中的元素逐个地出队列,入栈;全部入栈后再逐个出栈,入队列。
代码:
void Inverser(Stack &S, Queue &Q) {
ElemType x;
//本算法实现将队列中的元素逆置
while (!QueueEmpty(Q)) {
DeQueue(Q,x); //队列中全部元素依次出队
Push(S, x); //元素依次入栈
}
while (!StackEmpty(S)) {
Pop(S, x); //栈中全部元素依次出栈
EnQueue(Q, x); //再入队
}
}
*完整代码:
#include <stdio.h>
#include "stdlib.h"
#include <iostream>
using namespace std;
typedef int ElemType;
#define MaxSize 50 //定义队列中元素的最大个数
typedef struct {
ElemType data[MaxSize]; //存放栈中元素
int top; //栈顶指针
}Stack;
typedef struct {
ElemType data[MaxSize]; //存放队列元素
int front, rear; //队头指针和队尾指针
}Queue;
void InitStack(Stack &S) {
S.top = -1; //初始化栈顶指针
}
void InitQueue(Queue &Q) {
Q.rear = Q.front = 0; //初始化队首、队尾指针
}
bool QueueEmpty(Queue Q) {
if (Q.rear == Q.front) return true; //队空条件
else return false;
}
bool DeQueue(Queue &Q, ElemType &x) {
if (Q.rear == Q.front) return false; //队空,报错
x = Q.data[Q.front];
Q.front = (Q.front + 1) % MaxSize; //队头指针加1取模
return true;
}
bool Push(Stack &S, ElemType x) {
if (S.top == MaxSize - 1) //栈满,报错
return false;
S.data[++S.top] = x; //指针先加1,再入栈
return true;
}
bool StackEmpty(Stack &S) {
if (S.top == -1) //栈空
return true;
else //不空
return false;
}
bool Pop(Stack &S, ElemType &x) {
if (S.top == -1) //栈空,报错
return false;
x = S.data[S.top--]; //先出栈,指针再减1
return true;
}
bool EnQueue(Queue &Q, ElemType x) {
if ((Q.rear + 1) % MaxSize == Q.front) return false; //队满
Q.data[Q.rear] = x;
Q.rear = (Q.rear + 1) % MaxSize; //队尾指针加1取模
return true;
}
void Inverser(Stack &S, Queue &Q) {
ElemType x;
//本算法实现将队列中的元素逆置
while (!QueueEmpty(Q)) {
DeQueue(Q,x); //队列中全部元素依次出队
Push(S, x); //元素依次入栈
}
while (!StackEmpty(S)) {
Pop(S, x); //栈中全部元素依次出栈
EnQueue(Q, x); //再入队
}
}
void main() {
Stack S;
InitStack(S);
Queue Q;
InitQueue(Q);
EnQueue(Q, 100);
EnQueue(Q, 101);
Inverser(S, Q);
ElemType x;
DeQueue(Q, x);
printf("The first element is %d\n", x);
DeQueue(Q, x);
printf("The second element is %d\n", x);
system("pause");
}
-
-
- 第三题
-
利用两个栈S1,S2来模拟一个队列,已知栈的4个运算定义如下:
Push(S,x); //元素x入栈S
Pop(S,x); //S出栈并将出栈的值赋给x
StackEmpty(S); //判断栈是否为空
StackOverflow(S); //判断栈是否满
那么如何利用栈的运算来实现该队列的3个运算(形参由读者根据要求自己设计):
Enqueue; //将元素x入队
Dequeue; //出队,并将出队元素存储在x中
QueueEmpty; //判断队列是否为空
【算法思想】利用两个栈S1和S2来模拟一个队列,当需要向队列中插入一个元素时,用S1来存放已输入的元素,即S1执行入栈操作。当需要出队时,则对S2执行出栈操作。由于从栈中取出元素的顺序是原顺序的逆序,所以,必须先将S1中的所有元素全部出栈并入栈到S2中,再在S2中执行出栈操作,即可实现出队操作,而在执行此操作前必须判断S2是否为空,否则会导致顺序混乱。当栈S1和S2都为空时队列为空。
总结如下:
- 对S2的出栈操作用做出队,若S2为空,则先将S1中的所有元素送入S2。
- 对S1的入栈操作用做入队,若S1满,必须先保证S2为空,才能将S1中的元素全部插入S2中。
代码:
入队算法:
int EnQueue(Stack &S1, Stack &S2, ElemType x) {
if (!StackOverflow(S1)) {
Push(S1, x);
return 1;
}
if (StackOverflow(S1) && !StackEmpty(S2)) {
printf("队列满");
return 0;
}
if (StackOverflow(S1) && StackEmpty(S2)) {
while (!StackEmpty(S1)) {
Pop(S1, x);
Push(S2, x);
}
}
Push(S1, x);
return 1;
}
出队算法:
void DeQueue(Stack &S1, Stack &S2, ElemType &x) {
if (!StackEmpty(S2)) {
Pop(S2, x);
}
else if (StackEmpty(S1)) {
printf("队列为空");
}
else {
while (!StackEmpty(S1)) {
Pop(S1, x);
Push(S2, x);
}
Pop(S2, x);
}
}
判断队列为空的算法:
int QueueEmpty(Stack S1, Stack S2) {
if (StackEmpty(S1) && StackEmpty(S2))
return 1;
else
return 0;
}
栈: 括号匹配、表达式求值、递归
队列: 层次遍历
-
- 习题
- 第一题
- 习题
假设一个算数表达式中包含圆括号、方括号和花括号3种类型的括号,编写一个算法来判别表达式中的括号是否匹配,以字符”\0”作为算术表达式的结束符。
【算法思想】扫描每个字符,遇到花、中、圆的左括号进栈,遇到花、中、圆的右括号时检查栈顶元素是否为相应的左括号,若是,退栈,否则配对错误。 最后栈如果不为空也为错误。
代码:
bool BracketsCheck(char *str) {
Stack S;
ElemType e;
InitStack(S); //初始化栈
int i = 0;
while (str[i] != '\0') {
switch (str[i]) {
//左括号入栈
case '(': Push(S, '('); break;
case '[': Push(S, '['); break;
case '{': Push(S, '{'); break;
//遇到右括号,检测栈顶
case ')': Pop(S, e);
if (e != '(') return false;
break;
case ']': Pop(S, e);
if (e != '[') return false;
break;
case '}': Pop(S, e);
if (e != '{') return false;
break;
default:
break;
}//switch
i++;
}//while
if (!IsEmpty(S)) {
printf("括号不匹配\n");
return false;
}
else {
printf("括号匹配\n");
return true;
}
}
-
-
- 第二题
-
利用一个栈实现以下递归函数的非递归计算:
- n=0
Pn(x)= 2x n=1
2xPn-1(x)-2(n-1)Pn-2(x) n>1
【算法思想】设置一个栈用于保存n和对应的Pn(x),栈中相邻元素的Pn(x)有题中关系。然后边出栈边计算Pn(x),栈空后该值就计算出来了。
代码:
double P(int n, double x) {
struct stack {
int no; //保存n
double val; //保存pn(x)值
}st[MaxSize];
int top = -1, i; //top为栈st的下标值变量
double fv1 = 1, fv2 = 2 * x; //n=0,n=1时的初值
for (i = n; i >= 2; i--) {
top++;
st[top].no = i;
} //入栈
while (top >= 0) {
st[top].val = 2 * x*fv2 - 2 * (st[top].no - 1)*fv1;
fv1 = fv2;
fv2 = st[top].val;
top--; //出栈
}
return fv2;
}