<C> 链表 双向链表 栈 队列
一.链表
1.线性存储结构:
在一个结构体中 再放一个本类型(结构体类型)的指针
这个指针不指向自己 指向的是要找的下一个结构体的地址 以此类推 没有数量限制
2.声明及链表遍历:
1 #include<stdio.h> 2 3 typedef struct AA 4 { 5 int bh; 6 char* name; 7 char* tel; 8 struct AA* pNext; 9 }Node; 10 11 int main() 12 { 13 14 Node a = {1,"aa","111",NULL}; 15 Node b = {2,"bb","222",NULL}; 16 Node c = {3,"cc","333",NULL}; 17 18 Node* p = &a; 19 20 a.pNext = &b; 21 b.pNext = &c; 22 23 while(p != NULL) 24 { 25 printf("%d %s %s\n",p->bh,p->name,p->tel); 26 p = p -> pNext; 27 } 28 29 return 0; 30 }
注:
①代码中的p存的是这个结构体的地址 而不是这个结构体的指针
②在移动p的时候 不能用p++ 链表的存储不一定是连续的 连续的空间才可以用p++
3.链表添加:
思路:
①给节点申请空间 并赋值
②判断链表是否存在
分两种情况:一是不存在 那么这个新来的结点即是头 也是尾
二是存在 那么就让尾的下一个指向新来的这个结点(先连) 然后让尾指向新来的结点(后移)
1 #include<stdio.h> 2 #include<stdlib.h> 3 4 typedef struct NODE 5 { 6 int bh; 7 char* name; 8 char* tel; 9 struct NODE* pNext; 10 }Node; 11 12 void AddNode(int bh,char* name,char* tel,Node** ppHead,Node** ppEnd); 13 14 int main() 15 { 16 Node* pHead = NULL; 17 Node* pEnd = NULL; 18 19 AddNode(1,"aaa","111",&pHead,&pEnd); 20 AddNode(2,"bbb","222",&pHead,&pEnd); 21 AddNode(3,"ccc","333",&pHead,&pEnd); 22 AddNode(4,"ddd","444",&pHead,&pEnd); 23 24 return 0; 25 } 26 27 void AddNode(int bh,char* name,char* tel,Node** ppHead,Node** ppEnd) 28 { 29 Node* pNode = (Node*)malloc(sizeof(Node)); 30 pNode -> bh = bh; 31 pNode -> name = name; 32 pNode -> tel = tel; 33 pNode -> pNext = NULL; 34 35 if((*ppHead) == NULL) 36 { 37 *ppHead = pNode; 38 *ppEnd = pNode; 39 } 40 else 41 { 42 (*ppEnd) -> pNext = pNode; 43 *ppEnd = pNode; 44 } 45 46 return ; 47 }
注:
①整个链表是以第一个节点的地址存在的
②之所以链表有两个指针 一个头指针 一个尾指针 有了尾指针是为了省去遍历的过程 方便知道最后一个节点的位置
③在传参的时候 要注意 如果你想改变外面传进来的指针的值 那就得用二级指针
4.链表插入:
思路:
①头插:要插入的结点的下一个指向头 头指向新来的结点(要插入的结点)
②中间插入:循环遍历判断bh
如果等于 插入的结点的下一个指向标记的下一个 标记的下一个指向插入的结点
③尾插:尾的下一个等于新插入的结点 让新插入的结点作尾
1 #include<stdio.h> 2 #include<stdlib.h> 3 4 typedef struct NODE 5 { 6 int bh; 7 char* name; 8 char* tel; 9 struct NODE* pNext; 10 }Node; 11 12 int GetBh(); 13 Node* GetNode(); 14 void AddNode(Node** ppHead,Node** ppEnd,Node* pNode); 15 void InsertNode(Node** ppHead,Node** ppEnd,Node* pNode,int bh); 16 17 int main() 18 { 19 Node* pHead = NULL; 20 Node* pEnd = NULL; 21 22 AddNode(&pHead,&pEnd,GetNode()); 23 AddNode(&pHead,&pEnd,GetNode()); 24 AddNode(&pHead,&pEnd,GetNode()); 25 AddNode(&pHead,&pEnd,GetNode()); 26 27 InsertNode(&pHead,&pEnd,GetNode(),1); 28 InsertNode(&pHead,&pEnd,GetNode(),3); 29 InsertNode(&pHead,&pEnd,GetNode(),10); 30 31 return 0; 32 } 33 34 int GetBh() 35 { 36 static int i = 1; 37 return i++; 38 } 39 40 Node* GetNode() 41 { 42 Node* pNode = (Node*)malloc(sizeof(Node)); 43 pNode -> bh = GetBh(); 44 pNode -> name = "aaa"; 45 pNode -> tel = "111"; 46 pNode -> pNext = NULL; 47 48 return pNode; 49 } 50 51 void AddNode(Node** ppHead,Node** ppEnd,Node* pNode) 52 { 53 if(ppHead == NULL || ppEnd == NULL || pNode == NULL) 54 { 55 return ; 56 } 57 58 if((*ppHead) == NULL) 59 { 60 *ppHead = pNode; 61 *ppEnd = pNode; 62 } 63 else 64 { 65 (*ppEnd) -> pNext = pNode; 66 *ppEnd = pNode; 67 } 68 69 return ; 70 } 71 72 void InsertNode(Node** ppHead,Node** ppEnd,Node* pNode,int bh) 73 { 74 Node* pMark = *ppHead; 75 76 if((*ppHead) -> bh == bh) 77 { 78 pNode -> pNext = *ppHead; 79 *ppHead = pNode; 80 81 return ; 82 } 83 84 while(pMark -> pNext != NULL) 85 { 86 if(pMark -> pNext -> bh == bh) 87 { 88 pNode -> pNext = pMark -> pNext; 89 pMark -> pNext = pNode; 90 91 return ; 92 } 93 pMark = pMark -> pNext; 94 } 95 96 (*ppEnd) -> pNext = pNode; 97 *ppEnd = pNode; 98 99 return ; 100 }
注:对链表进行操作的时候 一定要先连后面的 再连前面的
5.链表删除:
思路:
①删除的是否是头节点:pDel = 头节点 然后让头指向头的下一个 释放 free 赋空 return
②中间删除:
首先遍历链表 要删除的是标记的下一个 标记的下一个指向要删除的下一个(标记的下一个的下一个)
特殊情况:删除的是尾结点 所以这里要多一个if判断 判断删除的是否是尾结点
1 #include<stdio.h> 2 #include<stdlib.h> 3 4 typedef struct NODE 5 { 6 int bh; 7 char* name; 8 char* tel; 9 struct NODE* pNext; 10 }Node; 11 12 int GetBh(); 13 Node* GetNode(); 14 void AddNode(Node** ppHead,Node** ppEnd,Node* pNode); 15 void DeleteNode(Node** ppHead,Node** ppEnd,int bh); 16 17 int main() 18 { 19 Node* pHead = NULL; 20 Node* pEnd = NULL; 21 22 AddNode(&pHead,&pEnd,GetNode()); 23 AddNode(&pHead,&pEnd,GetNode()); 24 AddNode(&pHead,&pEnd,GetNode()); 25 AddNode(&pHead,&pEnd,GetNode()); 26 27 DeleteNode(&pHead,&pEnd,1); 28 DeleteNode(&pHead,&pEnd,2); 29 DeleteNode(&pHead,&pEnd,4); 30 31 return 0; 32 } 33 34 int GetBh() 35 { 36 static int i = 1; 37 return i++; 38 } 39 40 Node* GetNode() 41 { 42 Node* pNode = (Node*)malloc(sizeof(Node)); 43 pNode -> bh = GetBh(); 44 pNode -> name = "aaa"; 45 pNode -> tel = "111"; 46 pNode -> pNext = NULL; 47 48 return pNode; 49 } 50 51 void AddNode(Node** ppHead,Node** ppEnd,Node* pNode) 52 { 53 if(ppHead == NULL || ppEnd == NULL || pNode == NULL) 54 { 55 return ; 56 } 57 58 if((*ppHead) == NULL) 59 { 60 *ppHead = pNode; 61 *ppEnd = pNode; 62 } 63 else 64 { 65 (*ppEnd) -> pNext = pNode; 66 *ppEnd = pNode; 67 } 68 69 return ; 70 } 71 72 void DeleteNode(Node** ppHead,Node** ppEnd,int bh) 73 { 74 Node* pDel = NULL; 75 Node* pMark = *ppHead; 76 77 if((*ppHead) -> bh == bh) 78 { 79 pDel = *ppHead; 80 *ppHead = (*ppHead) -> pNext; 81 free(pDel); 82 pDel = NULL; 83 84 return ; 85 } 86 87 while(pMark -> pNext != NULL) 88 { 89 if(pMark -> pNext -> bh == bh) 90 { 91 pDel = pMark -> pNext; 92 pMark -> pNext = pMark -> pNext -> pNext; 93 free(pDel); 94 pDel = NULL; 95 96 if(pMark -> pNext == NULL) 97 { 98 *ppEnd = pMark; 99 100 return ; 101 } 102 } 103 pMark = pMark -> pNext; 104 } 105 106 return ; 107 }
注:一定要先连后删
6.链表简单练习:
①如何判断两个链表是否重合:看尾结点的地址
②如何找到第一个重合的点:
例如A链表 7个结点 B链表 5个结点
那么A比B长两个 让A先往下走两个 然后A和B一起走 重合的时候就找到了
③一个单向链表 想找到倒数第k个结点 且只遍历一次
定义两个指针 让指针1先移动k次 然后让指针1和指针2开始同时移动
当指针1移动到尾巴的时候 指针2指的地方就是倒数第k个结点
二.双向链表
1.说明:
相比单向链表 就是结构体中 多了一个pLast的指针 指向上一个 双向链表较单向的会更好操作
2.声明及增加:
思路:
①申请结点:对结点进行赋值 num和两个指针(NULL)
②如果头为空 即链表不存在 新来的结点既是头也是尾
其他情况 让新来的结点的上一个指向尾
1 #include<stdio.h> 2 #include<stdlib.h> 3 4 typedef struct NODE 5 { 6 int num; 7 struct NODE* pLast; 8 struct NODE* pNext; 9 }Node; 10 11 void AddNode(); 12 13 int main() 14 { 15 Node* pHead = NULL; 16 Node* pEnd = NULL; 17 18 AddNode(&pHead,&pEnd,1); 19 AddNode(&pHead,&pEnd,2); 20 AddNode(&pHead,&pEnd,3); 21 22 return 0; 23 } 24 25 void AddNode(Node** ppHead,Node** ppEnd,int num) 26 { 27 Node* pMark = *ppHead; 28 Node* pNode = (Node*)malloc(sizeof(Node)); 29 pNode -> num = num; 30 pNode -> pLast = NULL; 31 pNode -> pNext = NULL; 32 33 if((*ppHead) == NULL) 34 { 35 *ppHead = pNode; 36 *ppEnd = pNode; 37 38 return ; 39 } 40 else 41 { 42 (*ppEnd) -> pNext = pNode; 43 pNode -> pLast = *ppEnd; 44 *ppEnd = pNode; 45 46 return ; 47 } 48 }
三.栈
1.特点:先进后出 FILO(first in last out)
2.声明一个栈顶:
这里我们只能对栈顶元素进行操作 一个就够 这是不同与链表的地方
1 Stack* pTop = NULL;
3.判断栈是否为空:
思路:
①如果pTop为空 返回TRUE
②如果pTop不为空 返回FASLE
注:在C中是没有bool类型的 但是我们可以自己定义一个 详情看下面的代码
4.压栈:(进栈 相当于链表的头插)
思路:
①申请结点 赋值
②让新来结点的下一个等于栈顶 让栈顶等于新来的结点
这里是不用做if判断栈是否为空的 如果进栈的顺序是1 2 3 那么栈中的顺序就是3 2 1
5.出栈:不是把这个结点删除 而是把这个头的指针返给我 方便我进行下一步的操作
写法一:(返回的是一个结点)
思路:
①定义一个Stack*类型的pMark指向栈顶
②判断栈是否为空 为空就return
③让栈顶指向栈顶的下一个 返回pMark
注:
判断栈是否为空的时候 条件是IsEmpty(*ppTop) == TRUE
写法二:(返回的是里面的值)
思路:
①定义一个int类型的变量num来接我们要返回的值
②定义一个Stack*类型的pDel来接栈顶元素
③先赋值 后删除
④return num这个数
把上面的三个功能放在下面一个代码中:
1 #include<stdio.h> 2 #include<stdlib.h> 3 4 typedef struct STACK 5 { 6 int num; 7 struct STACK* pNext; 8 }Stack; 9 10 typedef int BOOL; 11 #define TRUE 1 12 #define FALSE 0 13 14 BOOL IsEmpty(Stack* pTop); 15 void Push(Stack** pTop,int num); 16 Stack* Pop(Stack** pTop); 17 int PopInt(Stack** pTop); 18 19 int main() 20 { 21 Stack* pTop = NULL; 22 23 Push(&pTop,1); 24 Push(&pTop,2); 25 Push(&pTop,3); 26 27 Pop(&pTop); 28 printf("%d\n",PopInt(&pTop)); 29 30 return 0; 31 } 32 33 BOOL IsEmpty(Stack* pTop) 34 { 35 if(pTop == NULL) 36 { 37 return TRUE; 38 } 39 else 40 { 41 return FALSE; 42 } 43 } 44 45 void Push(Stack** pTop,int num) 46 { 47 Stack* pStack = (Stack*)malloc(sizeof(Stack)); 48 pStack -> num = num; 49 pStack -> pNext = NULL; 50 51 pStack -> pNext = *pTop; 52 *pTop = pStack; 53 54 return ; 55 } 56 57 Stack* Pop(Stack** pTop) 58 { 59 Stack* pMark = *pTop; 60 if(IsEmpty(*pTop) == TRUE) 61 { 62 return NULL; 63 } 64 else 65 { 66 *pTop = (*pTop) -> pNext; 67 return pMark; 68 } 69 } 70 71 int PopInt(Stack** pTop) 72 { 73 int num; 74 Stack* pDel = *pTop; 75 76 if(IsEmpty(*pTop) == TRUE) 77 { 78 return -1; 79 } 80 else 81 { 82 num = (*pTop) -> num; 83 *pTop = (*pTop) -> pNext; 84 free(pDel); 85 pDel = NULL; 86 87 return num; 88 } 89 }
四.队列
1.特点:先进先出 FIFO(first in first out)
2.入队(即尾部添加)
3.出队(即头部删除 返回值是一个结点)
1 #include<stdio.h> 2 #include<stdlib.h> 3 4 typedef struct QUEUE 5 { 6 int num; 7 struct QUEUE* pNext; 8 }Queue; 9 10 void Push(Queue** ppHead,Queue** ppEnd,int num); 11 Queue* Pop(Queue** ppHead,Queue** ppEnd); 12 13 int main() 14 { 15 Queue* pHead = NULL; 16 Queue* pEnd = NULL; 17 18 Push(&pHead,&pEnd,1); 19 Push(&pHead,&pEnd,2); 20 Push(&pHead,&pEnd,3); 21 22 printf("%d\n",(Pop(&pHead,&pEnd)) -> num); 23 printf("%d\n",(Pop(&pHead,&pEnd)) -> num); 24 printf("%d\n",(Pop(&pHead,&pEnd)) -> num); 25 26 return 0; 27 } 28 29 void Push(Queue** ppHead,Queue** ppEnd,int num) 30 { 31 Queue* pQueue = (Queue*)malloc(sizeof(Queue)); 32 pQueue -> num = num; 33 pQueue -> pNext = NULL; 34 35 if(*ppHead == NULL) 36 { 37 *ppHead = pQueue; 38 *ppEnd = pQueue; 39 } 40 else 41 { 42 (*ppEnd) -> pNext = pQueue; 43 *ppEnd = pQueue; 44 } 45 46 return ; 47 } 48 49 Queue* Pop(Queue** ppHead,Queue** ppEnd) 50 { 51 Queue* pDel = *ppHead; 52 53 if((*ppHead) == NULL) 54 { 55 return NULL; 56 } 57 else 58 { 59 *ppHead = (*ppHead) -> pNext; 60 if((*ppHead) == NULL) 61 { 62 *ppEnd = NULL; 63 } 64 return pDel; 65 } 66 }