链表是数据库的灵魂,可以说,其他所有的蛮复杂的数据结构,都能找到链表的影子。
链表部分属性
在这里,有必要区分一下首节点和头结点。
一般我们都可以理解首节点和尾节点,但是,有的时候,我们要让头结点指向首节点。这个头结点一般不存储数据,仅仅只是一个指向(首节点)。之所以,有一个头结点,是因为这个的存在有助于我们对链表的增删改查(代码更有可读性,简便代码)。
头结点和首节点不一定是一个数据类型,可能头结点仅仅是一个指针。
确认一个链表需要几个参数
只需要头结点
分类
算法
在这里,我的头结点和节点使用的是一个数据类型,当然,有人很多觉得这样不够优雅,但是,也没关系啦,毕竟,我只是想要写写算法而已。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
| #include <stdio.h> #include <stdlib.h> #include <stdbool.h>
typedef struct Node* PNODE; typedef struct Node NODE; struct Node { int data; struct Node *pNext; };
PNODE create(void); void show(PNODE pHead); void insert_tail(PNODE pHead, int data); void insert_head(PNODE pHead, int data); bool is_empty(PNODE pHead); int length(PNODE pHead);
int main(void) { PNODE pHead; int len;
pHead = create(); is_empty(pHead); printf("%d\n",length(pHead)); insert_tail(pHead, 10); insert_tail(pHead, 20); insert_tail(pHead, 30); insert_head(pHead, 0); printf("%d\n",length(pHead)); show(pHead); return 0; }
bool is_empty(PNODE pHead){ if(pHead->pNext == NULL){ printf("链表为空\n"); return false; } return true; }
int length(PNODE pHead){ int i = 0; PNODE pNode = pHead; while (pNode->pNext != NULL){ i++; pNode = pNode->pNext; } return i; }
PNODE create(void) { PNODE pHead = (PNODE) malloc(sizeof(NODE)); if (!pHead) { printf("分配失败,程序终止\n"); exit(-1); } pHead->pNext = NULL; return pHead; }
void show(PNODE pHead) { PNODE pNode = pHead->pNext; while (pNode->pNext) { printf("%d\n", pNode->data); pNode = pNode->pNext; } printf("%d\n", pNode->data); }
void insert_head(PNODE pHead, int data) { PNODE pNew = (PNODE) malloc(sizeof(NODE)); if (!pNew) { printf("分配失败,程序终止\n"); exit(-1); } pNew->data = data; pNew->pNext = pHead->pNext; pHead->pNext = pNew; }
void insert_tail(PNODE pHead, int data) { PNODE pNew = (PNODE) malloc(sizeof(NODE)); if (pNew == NULL) { printf("分配失败,程序终止\n"); exit(-1); } pNew->data = data; pNew->pNext = NULL; PNODE pNode = pHead; while (pNode->pNext != NULL) { pNode = pNode->pNext; } pNode->pNext = pNew; }
void traverse(PNODE pHead) {
}
|
尾插法
我在书写代码的过程中,曾被尾插法所困扰,请看下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void insert_tail(PNODE pHead, int data) { PNODE pNew = (PNODE) malloc(sizeof(NODE)); if (pNew == NULL) { printf("分配失败,程序终止\n"); exit(-1); } pNew->data = data; pNew->pNext = NULL; PNODE pNode = pHead->pNext; while (pNode != NULL) { pNode = pNode->pNext; } pNode = pNew; }
|
乍一看,这个尾插没有问题,但是,仔细一考量
PNODE pNode = pHead->pNext;
这个时候 pNode 指向 pHead->pNext 为 NULL ,但是,下面又将
pNode = pNew;
对 NULL 的地址进行赋值是无效的。
具体情况,你可以查看我下面的博文。
c 语言 | NULL 解析