数组的静态分配
struct SqList
{ ElemType data[MaxSize];
int length;
} ; //顺序表类型
数组的动态分配
struct SqList
{ ElemType *data;
int length;
} ; //顺序表类型
顺序表类型定义:
#define OK 1
#define ERROR -1
#define MAX_SIZE 100
typedef int Status ;
typedef int ElemType ;
初始化顺序表InitList( L )
该运算的结果是构造一个空的线性表L。实际上只需将length成员设置为0即可。
Status InitList( struct SqList * L ) {
L->data=( ElemType * )malloc(MAX_SIZE*sizeof( ElemType ) ) ;
L->length= 0 ;
return OK ;
}
初始化顺序表InitList( L )
该运算的结果是构造一个完整的线性表L。
Status InitList( struct SqList *L,int a[],int n) {
L->data=( ElemType* )malloc(MAX_SIZE*sizeof( ElemType ) ) ;
int i;
for(i=0;i<n;i++) L->data[i]=a[i];
L->length= n ;
return OK ;
}
void DestroyList(struct SqList *L)
{
free(L);
}
Status ListEmpty(struct SqList *L)
{
return (L->length==0);
}
Status ListLength(struct SqList *L)
{
return (L->length);
}
void DispList(struct SqList *L)
{ int i;
if (ListEmpty(L)) return;
for (i=0;i<L->length;i++)
printf("%d",L->data[i]);
printf("\n");
}
Status GetElem(struct SqList *L,int i)
{ ElemType e;
if (i<1 || i>L->length) return ERROR ;
e=L->data[i-1];
return e;
} //本算法的时间复杂度为O(1) 体现顺序表的随机读取特性
。。。。。。。
感觉太简单的后边就不弄上去了
Status ListInsert(struct SqList *L,int i,ElemType e)
{ int j;
if (i<1 || i>L->length+1)
return ERROR ; //参数错误时返回false
i--; //将顺序表逻辑序号转化为物理序号
for (j=L->length;j>i;j--) //将data[i..n]元素后移一个位置
L->data[j]=L->data[j-1];
L->data[i]=e; //插入元素e
L->length++; //顺序表长度增1
return OK; //成功插入返回true
}
Status ListDelete(struct SqList *L,int i)
{ int j;
if (i<1 || i>L->length) //参数错误时返回false
return ERROR;
i--; //将顺序表逻辑序号转化为物理序号
ElemType e=L->data[i];
for (j=i;j<L->length-1;j++) //将data[i..n-1]元素前移
L->data[j]=L->data[j+1];
L->length--; //顺序表长度减1
return e; //成功删除返回true
}
顺序存储结构的优点和缺点
优点
–无需为表示结点间的逻辑关系而增加额外的存储空间;
–可方便地随机读取表中的任一元素。
缺点
–插入或删除运算不方便,除表尾的位置外,在表的其它位置上进行插入或删除操作都必须移动大量的结点,效率较低;
–存储空间不灵活。由于顺序表要求占用连续的存储空间,当表长变化较大时,难以确定合适的存储规模。
双链表
1 s->next = p->next
2 p->next->prior = s
3 s->prior = p
4 p->next = s
typedef struct DNode //双链表结点类型
{ ElemType data;
struct DNode *prior; //指向前驱结点
struct DNode *next; //指向后继结点
} DLinkNode;
void ListInsert ( DLinkNode * L,int i,ElemType e)
{ int j=0;
DLinkNode * p=L, * s; //p指向头结点,j设置为0
while (j<i-1 && p!=NULL) //查找第i-1个结点
{ j++;
p=p->next;
}if (p==NULL) //未找到第i-1个结点,返回false
return ;
else //找到第i-1个结点*p,在其后插入新结点*s
{ s=(DLinkNode *)malloc(sizeof(DLinkNode));
s->data=e; //创建新结点 * s
s->next=p->next; //在*p之后插入 * s结点
if (p->next!=NULL) //若存在后继结点,修改其前驱指针
p->next->prior=s;
s->prior=p;
p->next=s;
}
}
结点删除:删除*p结点之后的一个结点
1 p->next->next->prior = p
2 p->next = p->next->next
void ListDelete(DLinkNode *L,int i)
{ int j=0; DLinkNode *p=L,*q; //p指向头结点,j设置为0
while (j<i-1 && p!=NULL) //查找第i-1个结点
{ j++;
p=p->next;
}
if (p==NULL) return ; //未找到第i-1个结点
else //找到第i-1个结点*p
{ q=p->next; //q指向第i个结点
if (q==NULL) //当不存在第i个结点时返回false
return ;
p->next=q->next; //从双单链表中删除*q结点
if (p->next!=NULL) //修改其前驱指针
p->next->prior=p;
free(q); //释放*q结点
}
}
双链表的建立
头插法
void CreateListF(DLinkNode *L,ElemType a[],int n)
{ DLinkNode *s; int i;
//头结点
L->prior=L->next=NULL; //前后指针域置为NULL
for (i=0;i<n;i++) //循环建立数据结点
{ s=(DLinkNode *)malloc(sizeof(DLinkNode));
s->data=a[i]; //创建数据结点*s
s->next=L->next; //将*s插入到头结点之后
if (L->next!=NULL) //若L存在数据结点,修改前驱指针
L->next->prior=s;
s->prior=L;
L->next=s;
}
}
尾插法
void CreateListR(DLinkNode *L,ElemType a[],int n)
{ DLinkNode *s,*r;
int i;
//头结点
r=L; //r始终指向尾结点,开始时指向头结点
for (i=0;i<n;i++) //循环建立数据结点
{ s=(DLinkNode *)malloc(sizeof(DLinkNode));
s->data=a[i]; //创建数据结点*s
r->next=s;
s->prior=r; //将*s插入*r之后
r=s; //r指向尾结点
}
r->next=NULL; //尾结点next域置为NULL
}
与非循环双链表相比,循环双链表:
链表中没有空指针域
p所指结点为尾结点的条件:p->next==L
一步操作即L->prior可以找到尾结点
栈的静态顺序存储表示
#define MaxSize 100 //最大存储量,无分号
#typedef int ElemType ;
typedef struct
{ ElemType data[MaxSize]; //静态,数组形式
int top; //栈顶指针
} SqStack;
约定top总是指向栈顶元素。
栈空条件:top=-1
进栈操作:top++; 将a放在top处
栈满条件:top=MaxSize-1
出栈操作:从top处取出元素e; top–;
(1)初始化栈InitStack(s)
建立一个新的空栈s,实际上是将栈顶指针指向-1即可。
void InitStack(SqStack *s)
{
s->top=-1;
}
注意:s为栈指针,top为s所指栈的栈顶指针
void ClearStack(SqStack *s)
{
s->top=-1;
}//清空栈
int StackEmpty(SqStack *s)
{
return (s->top==-1);
}//判空
入栈Push(s,e)
在栈不满的条件下,先将栈指针增1,然后在该位置上插入元素e。
int Push(SqStack *s,Elemtype e)
{ if (s->top== MaxSize-1) //栈满的情况,即栈上溢出
return -1;
s->top++; //栈顶指针增1
s->data[s->top]=e; //元素e放在栈顶指针处
return 1;
}
出栈Pop(s)
在栈不为空的条件下,先将栈顶元素赋给e,然后将栈指针减1。
取栈顶元素GetTop(s)
在栈不为空的条件下,返回栈顶元素。
int GetTop(SqStack *s)
{
if (s->top==-1) //栈为空的情况,即栈下溢出
return -1;
return s->data[s->top]; //返回栈顶指针指向的元素
}
【例】 设计一个算法利用顺序栈判断一个字符串是否是对称串。所谓对称串是指从左向右读和从右向左读的序列相同。
算法设计思路:
字符串str的所有元素依次进栈,产生的出栈序列正好与str的顺序相反。
int symmetry(ElemType str[])
{ int i; ElemType e;
SqStack st;
InitStack(&st); //初始化栈
for (i=0;str[i]!='\0';i++) //将串所有元素进栈
Push(&st,str[i]); //元素进栈
for (i=0;str[i]!=0;i++){
e=Pop(&st); //退栈元素e
if (str[i]!=e) //若e与当前串元素不同则不是对称串
{ printf("当前字符串不对称\n");
return -1;
}
}
printf("当前字符串对称\n");
return 1;
}
动态顺序栈
#define MaxSIZE 100 //栈初始向量大小
#define AddSIZE 10 //存储空间分配增量
typedef int ElemType ;
typedef struct sqstack{
ElemType *bottom; //栈不存在时值为NULL
ElemType *top; //栈顶指针
int stacksize ; //当前已分配空间,以元素为单位
}SqStack ;
SqStack Init_Stack()//栈的初始化
{ SqStack S ;
S.bottom=(ElemType *)malloc(MaxSIZE *sizeof(ElemType));
if (S.bottom==NULL) return 0;//栈内存分配是否成功
S.top=S.bottom ; //栈空时栈顶和栈底指针相同
S. stacksize=MaxSIZE;
return S ;
}
//入栈
int Push(SqStack *S , ElemType e){
if (S->top-S->bottom>=S->stacksize-1){ //栈满,追加存储空间 S->bottom=(ElemType *)
realloc(S->bottom,(AddSIZE+MaxSIZE)*sizeof(ElemType));
if ( S->bottom==NULL) return 0;
S->stacksize+=AddSIZE;
}
*S->top=e;
S->top++ ;//栈顶指针加1,e成为新的栈顶
return 1;
}
//出栈
Elemtype Pop(SqStack *S){
if ( S->top== S->bottom )
return 0; /* 栈空,返回失败标志 */
S->top-- ;
Elemtype e=*S-> top ;
return e;
}
栈的链式储存
栈空条件:s->next=NULL
栈满条件:不考虑
进栈操作:将新结点插入到头结点之后
退栈操作:取出头结点之后结点的元素并删除之
栈的插入与删除只能在同一端进行,同时满则后进先出原则。
typedef struct linknode{
ElemType data; //数据域
struct linknode *next; //指针域
} LinkStNode;
void initStack(LinkStNode *s)
{ //建立一个空栈s
s=(LinkStNode*)malloc(sizeof(LinkStNode));
s->next=NULL;
}
//s是头节点
void Push(LinkStNode *s,ElemType e)
{
LinkStNode *p;
p=(LinkStNode *)malloc(sizeof(LinkStNode));
p->data=e; //新建元素e对应的结点p
p->next=s->next; //p结点插入到头结点之后
s->next=p;
}
ElemType Pop(LinkStNode *s)
{ LinkStNode *p; ElemType e
if (s->next==NULL) return -1; //栈空的情况
p=s->next; //p指向首个数据结点
e=p->data;
s->next=p->next; //删除*p结点
free(p); //释放*p结点
return e;
}
ElemType GetTop(LinkStNode *s)
{
if (s->next==NULL) //栈空
return -1;
return s->next->data;
}
链栈的应用举例
【例】编写一个算法判断输入的表达式中括号是否配对(假设只含有左、右圆括号)。
表达式中的左右括号是按最近位置配对的。所以利用一个栈来进行求解。这里采用链栈。
例如:exp=“(()))”
① ‘(‘进栈
② ‘(‘进栈
③ 遇到’)’,栈顶为’(‘,退栈
④ 遇到’)’,栈顶为’(‘,退栈
⑤ 遇到’)’,栈为空,返回false
int Match(char exp[],int n)
{ int i=0;
int flag=1;
LinkStNode st;
InitStack(&st); //初始化栈
while (i<n && flag){ //扫描exp中所有字符
if (exp[i]=='(‘) Push(&st,exp[i]); //左括号进栈
else if (exp[i]==')') //当前字符为右括号
{ if (GetTop(st)!=-1)
{ Pop(&st); } //将栈顶元素出栈
else flag=0; } //空栈,表示不匹配
i++; } //继续处理其他字符
if (st.next!=NULL) flag=0;
return flag; }
串的静态顺序存储表示
#define MaxSize 100 //最大存储量,无分号
#typedef char ElemType ;
typedef struct
{ ElemType str[MaxSize+1]; //静态,数组形式
int length; //串长度
} StringType ;
//串的联结操作
StringType StrConcat ( StringType s, StringType t){
/* 将串t联结到串s之后,结果仍然保存在s中 */
int i;
if ((s.length+t.length)>MaxSize)
return 0; /* 联结后长度超出范围 */
for (i=1 ; i<=t.length ; i++)
s.str[s.length+i]=t.str[i] ; /* 串t联结到串s之后 */
s.length=s.length+t.length ; /* 修改联结后的串长度 */
return s;}
//求字串
int Index (StringType s, int pos, int len, StringType *sub){
/*用sub返回串s的第pos个字符起长度为len的子串。*/
int k, j ;
if (pos<1||pos>s.length||len<0||len>(s.length-pos+1))
return 0 ; /* 参数非法 */
sub->length=len ; /* 求得子串长度 */
for (j=1; j<=len ; j++)
sub->str[j]=s.str[pos -1+j] ; /* 逐个字符复制求得子串 */
return 1 ;
}
串的动态顺序存储表示
typedef struct
{
char *ch; //若非空,按长度分配,否则为null
int length; // 串的长度
} HString ;
串的联结操作
Hstring* StrConcat(HString *T, HString *s1, HString *s2){
/* 用T返回由s1和s2联结而成的串 */
int k, j ;
if (T->ch) free(T->ch); /* 释放旧空间 */
T->length=s1->length+s2->length;
if ((T->ch=(char *)malloc(sizeof((char)* T->length))==NULL))
{ printf(“系统空间不够,申请空间失败 !\n”) ;
return 0 ; }
for (j=0 ; j<s1->length; j++)
T->ch[j]=s1->ch[j] ; /* 将串s复制到串T中 */
for (k=s1->length, j=0 ; j<s2->length; k++, j++)
T->ch[k]=s2->ch[j] ; /* 将串s2复制到串T中 */
return 1;
}
串的链式存储表示
串的链式存储结构和线性表的串的链式存储结构类似,采用单链表来存储串:
data域:存放字符,data域可存放的字符个数称为结点的大小;
next域:存放指向下一结点的指针。
typedef struct StrNode
{
char data ;
struct StrNode *next;
}LinkStrNode ;
若每个结点仅存放一个字符,则结点的指针域就非常多,造成系统空间浪费。
为节省存储空间,考虑串结构的特殊性,使每个结点存放若干个字符,这种结构称为块链结构。
#define MaxSize 4
typedef struct StrNode
{ char data[MaxSize] ;
struct StrNode *next;
}LinkStrNode
//替换最先出现的ab为xyz
void Repl(LinkStrNode *s){
LinkStrNode *p=s->next,*q;
int find=0;
while (p->next!=NULL && find==0){ //查找ab子串
if (p->data==‘ a’ && p->next->data==‘b’)
{ p->data=‘x’; p->next->data=‘z’;
q=(LinkStrNode *)malloc(sizeof(LinkStrNode));
q->data=‘y’;
q->next=p->next; p->next=q;
find=1;
} else p=p->next;
}}
int BFIndex(SqString s,SqString t)
{ int i=1, j=1;
while (i<=s.length && j<=t.length)
{ if (s.data[i]==t.data[j]) //继续匹配下一个字符
{ i++; j++;} //主串和子串依次匹配下一个字符
else //主串、子串指针回溯重新开始下一次匹配
{ i=i-j+2; //主串从下一个位置开始匹配
j=1; } //子串从头开始匹配
}
if (j>=t.length) return(i-t.length); //返回匹配的第一个字符的下标
else return 0; //模式匹配不成功
}
int BFIndex(SqString s,SqString t)
{ int i=1, j=1,k;
while (i<s.length && j<t.length)
{ k=i;
if (s.data[i]==t.data[j]) //继续匹配下一个字符
{ i++; j++;} //主串和子串依次匹配下一个字符
else //主串、子串指针回溯重新开始下一次匹配
{ i=k+1; //主串从下一个位置开始匹配
j=1; } //子串从头开始匹配
}
if (j>=t.length) return(k); //返回匹配的第一个字符的下标
else return 0; //模式匹配不成功
}
KMP算法
int KMPIndex(SqString s,SqString t)
{ int next[MaxSize], i=1, j=1;
GetNext(t,next);
while (i<s.length && j<t.length)
{
if (j==0 || s.data[i]==t.data[j])
{ i++;
j++; //i、j各增1
}
else j=next[j]; //i不变,j后退
}
if (j>=t.length)
return(i-t.length); //返回匹配模式串的首字符下标
else
return(-1); //返回不匹配标志
}
void GetNext(SqString t,int next[])
{
i= 1; next[0] = 0; j = 0;
while( i<T.length){
if(j==0 || T.data[i] == T.data[j]){
++i; ++j;
next[i] = j;
}
else
j = next[j];
}
}
👴悟了 这个j=next[j]就是指最长相等前缀的最长相等前后缀
稀疏矩阵的顺序表
三元组结点定义
#define MAX_SIZE 101
typedef int elemtype ;
typedef struct
{ int row ; /* 行下标 /
int col ; / 列下标 /
elemtype value; / 元素值 */
}Triple ;
三元组顺序表定义
typedef struct
{ int rn ; /* 行数 /
int cn ; / 列数 /
int tn ; / 非0元素个数 */
Triple data[MAX_SIZE] ;
}TMatrix ;
矩阵转置:元素行列互换
采用矩阵的正常存储方式时,实现矩阵转置的经典算法:
void Trans(int a[N][M],int b[M][N]){
int i,j;
for(i=0;i<M;i++)
for(j=0;j<N;j++)
b[i][j]=a[j][i];
}
矩阵转置:列序递增转置法
算法思想:
按照三元组表A的列序(即转置后B的行号0~n-1)递增顺序转置。B行号为0时扫描A表,找出A中所有列号为0的元素,转置存入B;以此类推,直到所有行号均扫描完成。
TMatrix TransMatrix(TMatrix a , TMatrix b)
{ int i , j , col ;
/*初始化三元组表b.data的行、列数和非0元素个数 */
b.rn=a.cn ; b.cn=a.rn ; b.tn=a.tn ;
if (b.tn==0) printf(“ The Matrix A=0\n” );
else{ j=0; /*数组b新元素待存放位置,b.data[j]*/
for (col=0; col<a.cn ; col++)
/*按列遍历 a,每循环一次找到转置后的一个三元组 */
for (i=0 ;i<a.tn ; i++) /*逐个取数组a元素,循环次数是非0元素个数*/
if (a.data[i].col==col){
b.data[j].row=a.data[i].col ;
b.data[j].col=a.data[i].row ;
b.data[j].value=a.data[i].value;
j++ ; }
}return b;}
实例:A.rn=100,A.cn=500,A.tn=100
存储耗费:A.tn3=300(三元组形式)
时间耗费:A.cnA.tn=50000
与经典算法相比:存储降低,时间并未降低,原因是需要多次扫描矩阵A的三元组表。
思考:能否减少A扫描次数,一次扫描定位?
矩阵转置:一次定位快速转置法
算法思想:直接按照稀疏矩阵A的三元组表a.data的次序依次顺序转换,并将转换后的三元组放置于三元组表b.data的恰当位置。
前提:若能预先确定原矩阵A中每一列的(即B中每一行)第一个非0元素在b.data中应有的位置,则在作转置时就可直接放在b.data中恰当的位置。
因此,应先求得A中每一列的非0元素个数。
广义表的重要结论:
(3) 根据对表头、表尾的定义,任何一个非空广义表的表头可以是原子,也可以是子表, 而表尾必定是广义表。
(4) 广义表具有递归性。
typedef struct GLNode
{ int tag ; //标志域,为1:表结点;为0 :原子结点
union
{ elemtype value; /* 原子结点的值域 */
struct
{ struct GLNode *hp , *tp ;
} ptr ; /* ptr和atom两成员共用 */
}Gdata ;
} GLNode ; /* 广义表结点类型 */
树
二叉树的定义:度不大于2,左右有序
主要性质:
二叉树的第i层上至多有2i-1个结点(i≧1)
深度为k的二叉树至多有2k-1个结点
叶子结点数为n0,度为2的结点数为n2,则n0=n2+1
n个结点的完全二叉树深度为 ㏒2n +1
完全二叉树顺序:双亲 i/2、左孩子2i、右孩子2i+1
typedef struct BTNode
{ ElemType data ;
struct BTNode *Lchild , *Rchild ;
}BTNode ;
(1) 递归算法
void PreTraverse(BTNode *T)
{ if (T!=NULL) {
visit(T->data) ; /* 访问根结点 */
PreTraverse(T->Lchild) ;/* 访问左孩子 */
PreTraverse(T->Rchild) ;/* 访问右孩子 */
}
}
非递归算法
若二叉树为空,则返回;否则,令p=T;
⑴ 访问p所指向的结点;
⑵ q=p->Rchild ,若q不为空,则q进栈;
⑶ p=p->Lchild ,若p不为空,转(1),否则转(4);
⑷ 退栈到p ,转(1),直到栈空为止。
#define MAX_NODE 50
void PreTraverse( BTNode *T)
{ BTNode *Stack[MAX_NODE] ,*p=T, *q ;
int top=0 ;
if (T==NULL) printf(“ Binary Tree is Empty!\n”) ;
else { do
{ visit( p-> data ) ; q=p->Rchild ;
if ( q!=NULL ) stack[top++]=q ;
p=p->Lchild ;
if (p==NULL) { p=stack[top] ; top-- ; }
}while (p!=NULL) ;
}
}
#define MAX_NODE 50
void InorderTraverse( BTNode *T)//中序遍历
{ BTNode *Stack[MAX_NODE] ,*p=T ;
int top=0 , bool=1 ;
if (T==NULL) printf(“ Binary Tree is Empty!\n”) ;
else { do
{ while (p!=NULL)
{ stack[++top]=p ; p=p->Lchild ; }
if (top==0) bool=0 ;
else { p=stack[top] ; top-- ;
visit( p->data ) ; p=p->Rchild ; }
} while (bool!=0) ;
}
}