一、数据结构学习大纲。
1、数组与链表之间存储区别。
2、链表模型、使用场景。
3、链表种类?
传统链表 --> 单向链表、单向循环链表、双向链表、双向循环链表。
非传统链表 --> 内核链表
4、栈/队列
5、二叉树
无论学习什么数据结构,其实都是在学习该结构的增删改查。
二、回顾学习过一些数据结构 -- 增删改查。
变量: int x = 0;
增:x = 10;
删:x = 0;
改:x = 20
查:printf("%d\n",x);
数组:int x[3] = {0};
增:x[0] = 1 x[1] = 2 x[2] = 3
删:bzero(x,sizeof(x));
改:x[0] = 4 x[1] = 5 x[2] = 6
查:printf("x[0] = %d\n",x[0]);
printf("x[1] = %d\n",x[1]);
printf("x[2] = %d\n",x[2]);
三、数组与链表之间存储区别。
1、什么是数组?
1)数组空间在哪里申请?
如果数组是局部变量,那么该数组的空间就会在栈区申请。
如果数组是全局变量,那么该数组的空间就会在数据段申请。
2)数组的空间在内存中是连续的吗?
是。例如:int x[3] -> 在内存空间上连续申请12个字节,然后使用变量x间接访问这片内存空间。
3)数组在内存特点?
在内存空间中是连续的,所以只需要通过数组名+下标方式就可以访问任意一个成员。
因为下标其实就是数组首元素的地址往上偏移的单位数。
4)数组本身空间特点?
1>. 在定义数组时,必须声明数组的大小。 --> 限制了数组空间操作的大小。
2>. 在给数组元素赋值时,必须要检查好空间大小,不然容易越界访问一些未知区域。
2、什么是链表?
1)链表的空间是在哪里申请?
链表空间都是在堆区申请。
通过malloc函数申请空间,通过free函数释放空间。
2)链表的空间在内存是连续的吗?
不是连续,链表其实就是把那些零碎的空间组织起来,形成一个结构。
3)链表在内存特点?
由于链表每一个节点空间不是连续的,所以不能通过下标偏移来访问下一个成员。
每一个节点除了存储自身的数据(数据域)之外,还会存储下一个成员的地址(指针域),某一个成员就是通过本身的指针域中的地址来访问下一个成员的。
4)链表本身空间特点?
链表没有固定的长度,只要运行内存足够大,那么链表就可以无限延伸。
如果想在链表中新增新的节点,只需要申请新的空间,然后让最后那个节点存储新节点的地址就可以。
四、链表的模型。
1、链表的组成?
一个链表中会有非常多的成员组成,而每一个成员,我们都称之为节点。
头节点:排在第一个位置的节点称之为头节点。
头节点的特点: 数据域无效、指针域有效。
数据域无效 --> 数据域空间是存在的,但是不存储数据。
指针域有效 --> 指针域空间是存在的,也存储数据。
其他节点: 数据域有效,指针域也有效。
2、一个节点由什么组成?
由两部分组成:
数据域: 就是节点中存储自身数据的空间。 --> char/short/int/long/float/double/数组/指针/结构体
指针域: 就是节点中存储下一个节点地址的空间。 --> 指针
结论:数据域与指针域类型是不一样的,所以要描述一个节点,需要通过一个结构体来描述。
3、一个节点既有数据域,也有指针域,该如何描述?
struct list_node{
/* 数据域 */
/* 指针域 */
};
例子1: 假设链表中每一个节点都是存储一个int类型的数据,那么节点如何表示?
struct list_node{
int data; //数据域
struct list_node *next; //指针域
};
例子2: 假设链表每一个节点都是存储一张图片的名字/路径,那么节点如何表示?
struct list_node{
char picname[50];
struct list_node *next; //指针域
}
例子3: 假设链表中每一个节点都是存储一个小孩,那么节点如何表示?
struct list_node{
char name[50];
char passwd[50];
int age;
char tel[50];
int state;
time_t start_login_time;
float total_login_time;
time_t start_study_time;
float total_study_time;
time_t start_play_time;
float total_play_time;
int level;
int register_flags;
int login_flags;
char leave_say[50];
struct safe qa;
struct list_node *next; //指针域
};
结论:链表中数据域存什么,取决于你,你想存什么,就存什么。
链表指针域固定这么写的。
五、举例子。
假设有一条链表,每一个节点都是存储int类型的数据,分别依次是10,20,30,请把链表写出来。
1、设计链表中节点
由于每一个节点都是存放一个int类型的数据,所以节点应该表示如下:
struct list_node{
int data;
struct list_node *next;
};
2、初始化头节点。
1>. 为头节点申请空间。
2>. 为头节点空间赋值。
3、插入节点
1>. 为新节点申请空间
2>. 为新节点赋值
3>. 寻找最后一个节点
4>. 让最后一个节点指向新节点
----------------------------------------------------------------------------------------
#include <stdlib.h>
#include <stdio.h>
struct list_node{
int data; //数据域
struct list_node *next; //指针域
};
struct list_node *init_list_head()
{
//1. 为头节点申请空间
struct list_node *head = malloc(sizeof(struct list_node));
if(head == NULL)
printf("malloc head error!\n");
//2. 为头节点空间赋值
head->next = NULL;
//3. 将头节点的地址返回到主函数,给别的函数使用。
return head;
}
void list_add_tail(struct list_node *head,int num)
{
//1. 为新节点申请空间
struct list_node *new = malloc(sizeof(struct list_node));
if(new == NULL)
printf("malloc new error!\n");
//2. 为新节点的数据域与指针域赋值
new->data = num;
new->next = NULL;
//3. 寻找链表中最后一个节点
struct list_node *p = NULL;
for( p=head ; p->next!=NULL ; p=p->next);
//从循环中出来,p->next一定是NULL
//说明当前p指向的节点就是链表中最后一个节点
//4. 让最后一个节点的指针域存储着新节点的地址就可以。
p->next = new;
return;
}
int main(int argc,char *argv[])
{
//1. 初始化头节点
struct list_node *head = NULL;
head = init_list_head();
//2. 插入节点到链表末尾
list_add_tail(head,10);
list_add_tail(head,20);
list_add_tail(head,30);
list_add_tail(head,40);
list_add_tail(head,50);
return 0;
}
-----------------------------------------------------------------------
练习1: 完成 show_list_node(???) 函数接口
void show_list_node(struct list_node *head)
{
struct list_node *p = NULL;
for(p=head->next;p!=NULL;p=p->next)
{
printf("p->data:%d\n",p->data);
}
return;
}
练习2: 完成链表 list_add_head() 头插功能。
list_add_tail() -> 尾插:在链表的末尾新增一个节点。
list_add_head() -> 头插:在链表头节点之后插入新节点。
void list_add_head(struct list_node *head,int num)
{
//1. 为新节点申请空间
struct list_node *new = malloc(sizeof(struct list_node));
if(new == NULL)
printf("malloc new error!\n");
//2. 为新节点赋值。
new->data = num;
new->next = head->next;
head->next = new;
return;
}
练习3: 完成链表 delete_list_node() 删除功能。
----------------------------最终代码--------------------------
#include <stdlib.h>
#include <stdio.h>
struct list_node{
int data; //数据域
struct list_node *next; //指针域
};
struct list_node *init_list_head()
{
//1. 为头节点申请空间
struct list_node *head = malloc(sizeof(struct list_node));
if(head == NULL)
printf("malloc head error!\n");
//2. 为头节点空间赋值
head->next = NULL;
//3. 将头节点的地址返回到主函数,给别的函数使用。
return head;
}
void list_add_tail(struct list_node *head,int num)
{
//1. 为新节点申请空间
struct list_node *new = malloc(sizeof(struct list_node));
if(new == NULL)
printf("malloc new error!\n");
//2. 为新节点的数据域与指针域赋值
new->data = num;
new->next = NULL;
//3. 寻找链表中最后一个节点
struct list_node *p = NULL;
for( p=head ; p->next!=NULL ; p=p->next);
//从循环中出来,p->next一定是NULL
//说明当前p指向的节点就是链表中最后一个节点
//4. 让最后一个节点的指针域存储着新节点的地址就可以。
p->next = new;
return;
}
void show_list_node(struct list_node *head)
{
struct list_node *p = NULL;
for(p=head->next;p!=NULL;p=p->next)
{
printf("p->data:%d\n",p->data);
}
return;
}
void list_add_head(struct list_node *head,int num)
{
//1. 为新节点申请空间
struct list_node *new = malloc(sizeof(struct list_node));
if(new == NULL)
printf("malloc new error!\n");
//2. 为新节点赋值。
new->data = num;
new->next = head->next;
head->next = new;
return;
}
int search_list_node(struct list_node *head,int num)
{
struct list_node *p = NULL;
for(p=head->next;p!=NULL;p=p->next)
{
if(p->data == num)
{
printf("search node:%d\n",p->data);
return 0;
}
}
return -1;
}
int delete_list_node(struct list_node *head,int num)
{
struct list_node *p = NULL;
struct list_node *q = NULL;
for(q=head,p=head->next;p!=NULL;q=p,p=p->next)
{
if(p->data == num)
{
q->next = p->next;
free(p);
return 0;
}
}
return -1;
}
void delete_list(struct list_node *head)
{
struct list_node *p = NULL;
struct list_node *q = NULL;
for(p=q=head;p!=NULL;p=q)
{
q = p->next;
free(p);
}
}
int main(int argc,char *argv[])
{
//1. 初始化头节点
struct list_node *head = NULL;
head = init_list_head();
//2. 插入节点到链表末尾
list_add_tail(head,10);
list_add_tail(head,20);
list_add_tail(head,30);
//3. 遍历链表,将链表中每一个节点数据域都打印出来。
show_list_node(head); //10 20 30
//4. 头插一些节点
list_add_head(head,8);
list_add_head(head,5);
list_add_head(head,3);
show_list_node(head); //3 5 8 10 20 30
//5. 根据特征值查找某一个值。
search_list_node(head,20);
delete_list_node(head,10);
show_list_node(head); //3 5 8 20 30
//6. 删除整条链表。
delete_list(head);
return 0;
}