今天可能着重在双向链表、栈、队列的一些学习,入门级别的学习。
1.链表概念
链表是非连续、非顺序、的链式存储结构。由一些列节点组成,他们的逻辑顺序是通过链表中的指针链接顺序表示的。结点包括两部分,数据域与指针域,数据域用来存储相应的数据,指针域用来存储链表结点的逻辑关系。
2.双向链表
双向链表每个节点中有两个指针域,pre指针和next指针,pre指针指向该节点的前驱节点,其中特殊的是,表头节点的pre指针指向表尾节点;next指针指向该节点的后继节点。
链表一般分为带头节点和不带头节点的链表,带头节点链表更容易实现添加删除遍历节点操作,减少对第一个数据节点的特殊处理。
3.双向链表的遍历
3.1创建5个节点
3.2通过pre指针 和 next指针 将他们连接 在一起:
3.3 遍历他们,并打印节点的值:
代码实现:
#include <stdio.h>
typedef struct ListNode ListNode;
struct ListNode {
int data;
ListNode *pre;
ListNode *next;
};
//向前打印
void print_next(ListNode *root) {
ListNode *p = root;
printf("List value is:");
while (p) {
printf("[%d]", p->data);
p =p->next; //后驱节点地址的 指针域
}
printf("\n");
}
//向后打印
void print_pre(ListNode *root) {
ListNode *p = root;
printf("List value is:");
while (p) {
printf("[%d]", p->data);
p = p->pre;//前驱节点地址的 指针域
}
printf("\n");
}
int main() {
ListNode a;
ListNode b;
ListNode c;
ListNode d;
ListNode e;
//初始化节点
a.data = 1;
a.pre = NULL;
a.next = NULL;
b.data = 2;
b.pre = NULL;
b.next = NULL;
c.data = 3;
c.pre = NULL;
c.next = NULL;
d.data = 4;
d.pre = NULL;
d.next = NULL;
e.data = 5;
e.pre = NULL;
e.next = NULL;
// 链接节点
a.next = &b;
b.pre = &a;
b.next = &c;
c.pre = &b;
c.next = &d;
d.pre = &c;
d.next = &e;
e.pre = &d;
//打印
print_next(&a);
print_pre(&d);
getchar();
return 0;
}
p =p->next; //后驱节点地址的 指针域
}
printf("\n");
}
//向后打印
void print_pre(ListNode *root) {
ListNode *p = root;
printf("List value is:");
while (p) {
printf("[%d]", p->data);
p = p->pre;//前驱节点地址的 指针域
}
printf("\n");
}
int main() {
ListNode a;
ListNode b;
ListNode c;
ListNode d;
ListNode e;
//初始化节点
a.data = 1;
a.pre = NULL;
a.next = NULL;
b.data = 2;
b.pre = NULL;
b.next = NULL;
c.data = 3;
c.pre = NULL;
c.next = NULL;
d.data = 4;
d.pre = NULL;
d.next = NULL;
e.data = 5;
e.pre = NULL;
e.next = NULL;
// 链接节点
a.next = &b;
b.pre = &a;
b.next = &c;
c.pre = &b;
c.next = &d;
d.pre = &c;
d.next = &e;
e.pre = &d;
//打印
print_next(&a);
print_pre(&d);
getchar();
return 0;
}
结果如下:
前面画横线,实际上是用于填空,大家可以自己先谢谢。
4.双向链表的节点插入
我记得书上说a,b顺序不能颠倒,否则会出错。
代码:
5.双向链表的删除操作
1.建立待删除节点 前后两节点的连接关系
a、使待删除节点的前节点next指针指向待删除节点的后节点
b、使待删除节点的后节点的pre指针指向待删除节点的前节点
2、释放删除节点
注意:上面的黑色实线箭头可以不用管理的,进行上面的操作后,黑色实线会自动断掉,这个是我在书上看到的,如果觉得错了的朋友欢迎留言指正。 (我前面那句话错了,准备描述是黑实线在free函数后就自动断了)
代码:
6.双向链表的基本操作测试代码
#include<stdio.h>
#include<malloc.h>
typedef struct ListNode ListNode;
struct ListNode{
int data;
ListNode *pre;
ListNode *next;
};
void list_insert(ListNode *pre_node, int data){
ListNode *next_node = pre_node->next;
ListNode *new_node = (ListNode*)malloc(sizeof(ListNode));
new_node->data = data;
new_node->pre= pre_node;
new_node->next = next_node;
pre_node->next = new_node;
if(next_node){
next_node->pre = new_node;
}
}
void list_delete(ListNode * delete_node){
if(delete_node->pre){
delete_node->pre->next = delete_node->next;
}
if(delete_node->next){
delete_node->next->pre = delete_node->pre;
}
free(delete_node);
}
void print(ListNode *root){
ListNode *p = root;
printf("List value is: ");
while(p){
printf("[%d] ", p->data);
p = p->next;
}
printf("\n");
}
int main(){
ListNode a;
ListNode b;
ListNode c;
a.data = 1;
a.pre = NULL;
a.next = NULL;
b.data = 2;
b.pre = NULL;
b.next = NULL;
c.data = 3;
c.pre = NULL;
c.next = NULL;
a.next = &b;
b.pre = &a;
b.next = &c;
c.pre = &b;
print(&a);
list_insert(&a, 999);
print(&a);
list_insert(&b, 888);
print(&a);
list_insert(&c, 777);
print(&a);
list_delete(a.next);
print(&a);
return 0;
}
结果:
之前运行这段代码的时候出错了,如下图:
野指针呀!!!,原因就是:下面的代码写错了。更正后好啦,所以会debug很重要很重要!!!会改代码的第一条就是会debug,这个是技能呀,影响着正常发挥。
上分代码只能删除 添加的节点,因为对于
ListNode a;
ListNode b;
ListNode c;
这三个节点是不能free的,而动态分配出来的节点是可以free的。所以上份代码只能用于删除动态分配的节点。
链表结构直接使用不多,有链表基础知识后,通过链表构造更加复杂的数据结构,如:队列,栈,堆,二叉树,图等数据结构。