根据数据结构书上来复习(提醒一下自己,学会自己总结,而不是去看很多试题,如果掌握原理了,就不用担心任何出题形式了。一定要注重基础)
一:线性表
1,线性表的两种表示和实现
1)顺序表示和实现
顺序表示是用一组地址连续的存储单元依次存储线性表的数据元素。顺序存储结构一般用数组来描述,由于线性表的长度可变,所以用动态分配的一维数组
1 #include<stdio.h> 2 #include<stdlib.h> 3 #define LIST_INIT_SIZE 100 4 #define LISTAdd 10 5 typedef struct{ 6 char *elem;//暂且用char 7 int length;//当期长度 8 int listsize;//当前分配的存储容量 9 }SqList; 10 void InitList(SqList &L) 11 { 12 //初始化 13 L.elem=(char *)malloc(LIST_INIT_SIZE*sizeof(char)); 14 if(!L.elem) 15 exit(0);//正常退出,0表示正常退出,其他表示非正常退出 16 L.length=0; 17 L.listsize=LIST_INIT_SIZE; 18 } 19 20 21 //线性表的插入,在第i个元素之前插入 22 void ListInsert(SqList &L,int i, char e) 23 { 24 //if((i<1)||(i>L.length+1)) 25 // return 0; 26 if(L.length>=L.listsize) 27 { 28 char *newbase=(char *)realloc(L.elem,(L.listsize+LISTAdd)*sizeof(char)); 29 if(!newbase) 30 exit(0); 31 //return 0在一个函数中是正常的返回过程,它会使得程序返回到函数被调用处,回复之前的执行流程, 32 //而exit 一般是在任意位置和使用的,执行到exit 0时, 33 //整个进程就over了(这也是为什么在多线程程序中不要随意使用exit的原因),用来使程序退出运行,一般用来处理(不可挽回的)错误状态。 34 //2. return是语言级别的,它表示了调用堆栈的返回;而exit是系统调用级别的,它表示了一个进程的结束。 35 //3. return是函数的退出(返回);exit是进程的退出。 36 // 4. return是C语言提供的,exit是操作系统提供的(或者函数库中给出的)。 37 //5. return用于结束一个函数的执行,将函数的执行信息传出个其他调用函数使用; 38 //exit函数是退出应用程序,删除进程使用的内存空间,并将应用程序的一个状态返回给OS,这个状态标识了应用程序的一些运行信息,这个信息和机器和操作系统有关,一般是 0 为正常退出,非0 为非正常退出。 39 L.elem=newbase; 40 L.listsize+=LISTAdd; 41 } 42 char *q=&(L.elem[i-1]); 43 char *p; 44 for(p=&(L.elem[L.length-1]);p>=q;--p) 45 *(p+1)=*p;//插入位置及之后的元素往后移 46 *q=e; 47 L.length++; 48 } 49 //从线性表中删除第i个元素,并用e返回其值 50 void ListDelete(SqList &L,int i,char &e) 51 { 52 //if((i<1)||(i>L.length)) 53 //return 0; 54 char *p=&(L.elem[i-1]); 55 e=*p; 56 char *q=&(L.elem[L.length-1]);//表尾元素的位置 57 for(++p;p<=q;++p) 58 *(p-1)=*p;//被删除元素之后的元素左移 59 L.length--; 60 } 61 int main(void) 62 { 63 SqList L; 64 int k=0; 65 char e; 66 InitList(L); 67 printf("请输入要插入线性表中的个数:"); 68 scanf("%d",&k); 69 for(int j=1;j<=k;j++) 70 { 71 char c; 72 printf("输入第%d个元素 ",j-1); 73 scanf(" %c",&c);//scanf("%c",&c) 与 scanf(" %c",&c),后者只是在%前多了个空格,似乎没有什么区别,但使用起来区别是很大的。 74 //scanf()作单字符输入时规定只接收一个字符,但它却把回车符也作为字符对待的。 75 //这就造成程序中只有一个输入字符的scanf()语句时,问题还不大,但如果后面还跟着第二个scanf()字符输入语句,这个scanf()就把前面输入的驾车符当作输入字符了。这就在输入逻辑上造成了混乱,达不到人们预期的愿望。有了这个空格,因为scanf()是跳过空格读字符的,就回避了这个问题。实践证明,这个空格放在%c后面也不能达到目的。应当说,这也是比较巧妙的应用! 76 ListInsert(L,j,c); 77 } 78 //ListInsert(L,1,'a'); 79 //ListInsert(L,2,'v'); 80 //ListInsert(L,3,'q'); 81 //ListInsert(L,4,'t'); 82 printf("你想删除第几个元素呢? "); 83 scanf("%d",&k); 84 ListDelete(L,k,e);// 85 printf("线性表中的元素的值是: "); 86 for(int i=0;i<L.length;i++) 87 printf("%c ",L.elem[i]); 88 printf("你删除的元素是:%c ",e); 89 return 0; 90 }
运行结果:
2)链表表示和实现
链式存储结构:用一组任意的存储单元存储线性表的数据元素(这组存储单元可以使连续的也可以是不连续的),n个结点链结成一个链表,即为线性表的链式存储结构。
两者的区别:线性表的顺序存储结构中,逻辑上相邻的两个元素在物理位置上紧邻,每个元素的存储位置都可从线性表的起始位置计算可得。在单链表中,任何两个元素之间没有固定的关系,但是每个元素的存储位置都包含在其直接前驱结点的信息中。前者是随机存取,后者是非随机存取。
1 #include<stdio.h> 2 #include<stdlib.h> 3 typedef struct LNode{ 4 char data;//暂且用char 5 struct LNode *next; 6 }LNode,*LinkList; 7 8 //带头结点的单链表线性表L中第i个位置之后插入元素e 9 void ListInsert_L(LinkList &L,int i, char e) 10 { 11 LNode *p,*s; 12 p=L; 13 int j=0; 14 while(p&&j<i-1) 15 { 16 p=p->next; 17 ++j;//找到第i-1个结点 18 } 19 s=(LinkList)malloc(sizeof(LNode));//生成新结点 20 s->data=e; 21 s->next=p->next;//插入L中 22 p->next=s; 23 } 24 //带头结点的单链表线性表L中删除第i个位置的元素,并由e返回其值 25 void ListDelete(LinkList &L,int i,char &e) 26 { 27 LNode *p,*q; 28 p=L; 29 int j=0; 30 while(p->next&&j<i-1) 31 { 32 p=p->next; 33 j++; 34 } 35 q=p->next;//删除p后面的元素 36 p->next=q->next; 37 e=q->data; 38 free(q); 39 } 40 int main(void) 41 { 42 LinkList L,PrintL; 43 int k; 44 char e; 45 L=(LinkList)malloc(sizeof(LNode));//生成头结点 46 L->next=NULL; 47 printf("要输入链表中的数据的个数为: "); 48 scanf("%d",&k); 49 for(int i=1;i<=k;i++) 50 { 51 printf("输入第%d个元素 ",i); 52 char c; 53 scanf(" %c",&c); 54 ListInsert_L(L,i,c); 55 } 56 printf("你要删除的元素的位置是: "); 57 scanf("%d",&k); 58 ListDelete(L,k,e); 59 printf("链表中的数据是: "); 60 PrintL=L->next; 61 while(PrintL) 62 { 63 printf("%c ",PrintL->data); 64 PrintL=PrintL->next; 65 } 66 printf("你删除的元素是:%c ",e); 67 return 0; 68 }
二:栈和队列
栈和队列也是线性表,其特殊性在于栈和队列的基本操作时线性表操作的子集,它们是操作受限的线性表,可称为限定性的数据结构。
1)栈的基本操作:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #define stack_init_size 100//预编译 4 #define stackAdd 10 5 /* 6 http://blog.sina.com.cn/s/blog_861912cd0101hzrb.html 预编译详解 7 */ 8 typedef struct { 9 char *base;//暂且用char,在栈构造之前和销毁之后,base的值为NULL 10 char *top;//栈顶指针 11 int stacksize;//当前已分配的存储空间 12 }SqStack; 13 14 void InitStack(SqStack &s) 15 { 16 s.base=(char *)malloc(stack_init_size * sizeof(char)); 17 if(!s.base) 18 exit(0); 19 s.top=s.base; 20 s.stacksize=stack_init_size; 21 } 22 void Push(SqStack &s,char e) 23 { 24 if(s.top-s.base>=s.stacksize) 25 { 26 s.base=(char *)realloc(s.base,(s.stacksize+stackAdd)*sizeof(char)); 27 if(!s.base) 28 exit(0); 29 s.top=s.base+s.stacksize; 30 s.stacksize+=stackAdd; 31 } 32 *s.top++ =e; 33 } 34 35 void Pop(SqStack &s,char &e) 36 { 37 if(s.top==s.base) 38 printf("栈中无元素!"); 39 e=* --s.top; 40 } 41 42 43 int main(void) 44 { 45 SqStack s; 46 char e; 47 InitStack(s); 48 Push(s,'a'); 49 Pop(s,e); 50 printf("%c ",e); 51 return 0; 52 }
2)队列的基本操作:
1 #include<stdio.h> 2 #include<stdlib.h> 3 typedef struct QNode{ 4 char data; 5 struct QNode *next; 6 }QNode,*QueuePtr; 7 typedef struct{ 8 QueuePtr front;//队头指针 9 QueuePtr rear;//队尾指针 10 }LinkQueue; 11 12 void InitQueue(LinkQueue &Q) 13 { 14 Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode)); 15 Q.front->next=NULL; 16 //Q.rear->next=NULL; 17 } 18 void EnQueue(LinkQueue &Q,char e) 19 { 20 QueuePtr p; 21 p=(QueuePtr)malloc(sizeof(QNode)); 22 if(!p) 23 exit(0); 24 p->data=e; 25 p->next=NULL; 26 Q.rear->next=p; 27 Q.rear=p;//队尾指向新进来的元素 28 } 29 void DeQueue(LinkQueue &Q,char &e) 30 { 31 //若队列不空,则删除Q的队头元素,用e返回其值 32 if(Q.rear==Q.front) 33 printf("队列为空!"); 34 QueuePtr p; 35 p=Q.front->next; 36 e=p->data; 37 Q.front->next=p->next; 38 if(Q.rear==p) 39 Q.front=Q.rear;//删除队列中最后一个元素 40 free(p); 41 } 42 int main(void) 43 { 44 LinkQueue q; 45 char e; 46 InitQueue(q); 47 EnQueue(q,'a'); 48 DeQueue(q,e); 49 printf("%c ",e); 50 return 0; 51 }