ZCMU-Data Structure (C language)
Part 1 Stack and Queue
1、栈和队列也是线性表,特殊性在于栈和队列的基本操作是线性表操作的子集,是操作受限的线性表。
2、栈是限定仅在表尾(栈顶)进行插入或者删除操作的线性表,它是先进后出(后进先出)的线性表。
3、队列是一种先进先出的线性表。只允许在队尾进行插入,在队头进行删除。
4、顺序栈的表示与实现
1)初始化
s.base=new SElemType[MAXSIZE];//为顺序栈动态分配一个最大容量为MAXSIZE的数组空间
if(!s.base) exit(OVERFLOW);//存储分配失败
s.top=s.base;//top初始为base,空栈
s.stacksize=MAXSIZE;//置栈的最大容量为MAXSIZE
return OK;
2)入栈
if(s.top-s.base==s.stacksize) return ERROR;//栈满
*s.top++=e;
return OK;
3)出栈
if(s.top==s.base) return ERROR;//栈空
e=*--s.top;//栈顶指针减1,将栈顶元素赋给e
return OK;
4)取栈顶元素
if(s.top!=s.base)//栈非空
return *(s.top-1);//返回栈顶元素的值,栈顶指针不变
5、链栈的表示与实现
通常,链栈用单链表表示,由于栈主要的操作是在栈顶进行插入或删除操作,显然链表最好,且没必要向单链表一样附加一个头结点。
1)初始化
无需设头结点
s=NULL;
return OK;
2)入栈
链栈入栈前不需要判断栈是否满,只需要为入栈元素动态分配一个结点空间。
p=new StackNode;//生成新结点
p->data=e;//将新结点数据域置为e
p->next=s;//将新结点插入栈顶
s=p;//修改栈顶指针为p;
return OK;
3)出栈
链栈在出栈前需要判断栈是否为空,但是链栈在出栈后需要释放栈元素的栈顶空间。
if(s==NULL) return ERROR;//栈空
e=s->data;//将栈顶元素赋给e
p=s;//将p临时保存栈顶元素空间,以备释放
s=s->next;//修改栈顶指针
delete p;
return OK:
4)取栈顶元素
if(S!=NULL)
return s->data; //返回栈顶元素的值,栈顶指针不变
6、循环队列-队列的顺序表示和实现
在非空队列中,头指针始终指向队列头元素,尾指针始终指向队列尾元素的下一个位置
1)循环队列初始化
1)循环队列初始化
q.base=new QElemType[MAXSIZE];
if(!q.base) exit(OVERFLOW);
q.front=q.rear=0;//头指针和尾指针为0,队列为空
return OK;
2)求循环队列的长度
return (q.rear-q.front+MAXSIZE)%MAXSIZE;
3)入队
if((q.rear+1)%MAXSIZE==q.front)//判断队满
return ERROR;
q.base[q.rear]=e;//新元素插入队尾
q.rear=(q.rear+1)%MAXSIZE;//队尾指针+1
return OK;
4)出队
if(q.front==q.rear) return ERROR;//队空
e=q.base[q.front];//保持队头元素
q.front=(q.front+1)%MAXSIZE;//队头指针+1
return OK;
5)取队头元素
if(q.front!=q.rear)//队列非空
return q.base[q.front];//返回队头元素的值,队头指针不变
7、链队-队列的链式表示和实现
示意图:
1)初始化
Q.rear=(QueuePtr)malloc(sizeof(QNode) );
Q.front = Q.rear;
if(!Q.front) return(OVERFLOW);
Q.front -> next = NULL;
return OK;
2)入队
链队在入队前不需要判断队是否满,需要为入队元素动态分配一个结点
p = ( QueuePtr )malloc(sizeof(QNode));
if(!p) return(OVERFLOW);
p->data = e;
p -> next = NULL;
Q.rear ->next = p;
Q.rear = p;
return OK;
3)出队
if(Q.front == Q.rear) retrun ERROR;
p = Q.front -> next;
e = p->data;
Q.front->next = p->next;
if(Q.rear == p) Q.rear = Q.front ;
free(p);
return OK;
链队出队操作时还需要考虑当队列最后一个元素被删后,队列尾指针也丢失,因此需要对队尾指针重新赋值(指向头结点)。
4)取队头元素
if(Q.front!=Q.rear)
return Q.front->next->data;//队头指针不变
Part 2 String and Array
9、串的模式匹配算法
例如
T=
“
abcac
”
; S=
“
ababcabcacbab
”
- 第一趟匹配 a b a b c a b c a c b a b (i=3)
- a b c (j=3)
- 第二趟匹配 a b a b c a b c a c b a b (i=2)
- a (j=1)
- 第三趟匹配 a b a b c a b c a c b a b (i=7)
- a b c a c (j=5)
- 第四趟匹配 a b a b c a b c a c b a b (i=4)
- a (j=1)
- 第五趟匹配 a b a b c a b c a c b a b (i=5)
- a (j=1)
- 第六趟匹配 a b a b c a b c a c b a b (i=11)
- a b c a c (j=6) 成功!
9、KMP算法
- KMP算法的改进在于:
- 每一趟匹配过程中出现字符比较不等时,不需要回朔i指针
- 只要利用已经“部分匹配”结果,调整j指针,即将模式向右滑动尽可能远的一段距离,来个提高算法效率。
- 上例的KMP算法匹配过程示意如下
- 第一趟匹配 a b a b c a b c a c b a b (i=3)
- a b c (j=3)
- 第二趟匹配 a b a b c a b c a c b a b (i=3 -> 7)
- a b c a c (j=1 -> 5)
- 第三趟匹配 a b a b c a b c a c b a b (i=7 -> 11)
- (a)b c a c (j=2 -> 6)
- 显然算法复杂度为O(n+m)
code:
int Index_KMP(SString S, SString T, int pos)
{ // 利用模式串T的next函数求T在主串
// S中第pos个字符之后第一次出现的位置;否则返回0。
i = pos; j = 1;
while(i<=S[0] && j <= T[0]){
if(j == 0 || S[i] == T[j]){ ++i; ++j;} // 继续比较后续字符
else j = next[j]; // 模式串向后移
}
if(j > T[0]) return i-T[0]; // 匹配成功
else return 0; // 匹配不成功
} // Index_KMP
11、数组计算方法以例题形式给出
12、特殊矩阵的压缩存储(数据结构 严蔚敏 P100-102)