链表结构
链表结构由节点构成,每个节点中包含指向下一节点的指针
struct Node{
int value; //存储的数据
struct Node* next; //指向下一节点的指针
//struct Node* last; //双链表中还有指向下一节点的指针
};
链表有一个特点:只要保存了链表的头指针,就能往下顺着捋出整条链表
struct LinkList{
struct Node* head;
}
这基本上就是链表的结构了
其实也不用 struct Node* head就可以表示这个链表了
基本操作
新建列表
新建一个列表...操作没啥好说的,开辟一个链表空间,然后初始化
struct LinkList* createList() {
struct LinkList* list = (struct LinkList*) malloc(sizeof(struct LinkList));
list->head = NULL;
return list;
}
增加节点
A------>B------->C----->NULL
现在是这样一条链表,A是头节点,我想加入D在C后头,让链表变成
A------>B------->C----->D------>NULL
那就新建一个节点D
为了方便常用,写个新建节点函数
struct Node* CreateNode(int data){
struct Node* node = (struct Node*)malloc(sizeof(struct Node));
node->value = data;
node->next =NULL;
return node;
}
那我第一步就新建一个节点取名叫 nodeD
输入的函数肯定是两个了,
一个是Node* head,我得知道我往哪个链表里加节点
一个是data,我得知道我在这里新建的节点里放什么数据
然后思路就是:
从前往后遍历,找到最后的node C
让C的next指针指向D, D指向空.....逆天简单
coding!
struct Node* append(Node* head, int data){
struct Node* node = CreateNode(data); //创建这个新的点准备加进来
if(head ==NULL){
return node;
} //如果原链表是空的,直接返回node node就是这个链表的全世界辣
struct Node* cur = head; //找一个过程变量,把最开始的head记下来 别待会head丢了
// 从head开始往后找 找到链表的尾巴C
while(cur->next !=NULL){
cur = cur->next; //直到找到最后面的节点 C
}
cur->next =node;
return head;
}
deal!
删除节点
感觉增加和删除原理一毛一样...让A滚,B的last指针指向空...
void deleteNode(struct Node* head, int key){
struct Node* cur = head;
while(cur->next!= NULL && cur->next->value != key){
cur->next = temp; //遍历到要删除的节点的前一个节点
}
if(cur->next != NULL){
struct Node* temp = cur->next; //temp 等于要删除的节点
cur->next = temp->next; //现在的节点的下一个指针指向它原本的下下个指针
free(temp); //释放掉内存
}
}
改、查
改查不用什么算法,遍历就行了...
链表的特点
链表和数组
链表的优势
插入和删除的效率高。靠指针连接,如果插入/删除只需要动前后点的指针即可。而数组需要整个空间变动。
大小可以动态增长。一般尤其是C中的数组都是固定大小的。
不连续,空间利用比较灵活
数组的优势
访问效率高。比如100个数字的数组里,我想找97号数字是多少,直接看下标就过去了。但是链表就需要从头节点开始遍历到97号去,时间复杂度O(n)。
空间开销小。数组里只有data,没有*next。
空间利用率高。数组是连续存放的。
单链表和双链表
单链表的优势,很显然就是占用内存小,缺点就是无法反向遍历,必须从head开始往后遍历...
双链表反之,占用内存大(因为每个节点需要存放next和last两个指针),但是可以双向遍历。
总结
链表太基础了...