de数据结构课

数组的静态分配
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.cn
A.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) ;
           }
 }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值