简单理解为链表的功能与数组功能相似用来存储数据,链表作为一种基本的数据结构在程序开发过程当中经常会使用到。对C语言来说链表的实现主要依靠结构体(可以存储多种数据类型)和指针,所以本文相关内容和程序需要有C语言当中指针和结构体的基础。
1、解决数组无法存储多种数据类型的问题。
2、解决数组中,元素个数无法改变的限制。
3、数组移动元素的过程中,要对元素进行大范围的移动,很耗时间,效率也不高。
1、链表由节点组成。
2、节点由值域与指针域组成。
3、还需要一个头指针指向链表(例如一个指针指向一个数组,那么数组名也可以代表此数组的首地址)
4、链表的基本表示
struct node //表示图片中的体
{
int num;
char a;
struct node *next; //指向下一个相同数据类型的结构体,next存放下一个结点的地址
}
> head头指针指向头结点,其中头结点的next指针指向下一个节点的头,一次类推直到最后一个节点的尾指针指向NULL(空指针)时离链表结束。
(这里的体是结构体类型,next是指向下一个结点的指针,结点的组成:数据域(体是数据域的一种体现)+(指针域) )
1、链表结点可以连续,可以不连续存储。
2、结点的逻辑顺序与物理顺序可以不一致。
3、表可扩充。
解释:各个结点的物理存储位置可能不是连续的,a0后面是a2而不是a1,但是每个结点的尾地址都是按照逻辑顺序指向下一个结点的。(意思就是说我有10个抽屉,每个抽屉编号1~10,但是这10个抽屉不是按顺序摆放,将扑克牌A到10一次按抽屉顺序放入10个抽屉里,每次取按顺序取出,例如第一个抽屉在第1层,里面是A,接下来要去2但是存放2的抽屉在第5层因此要去第5层取2)
1、
将一个新的结点插入到最后一个节点的后面,先将前一个尾结点的地址指向新结点的地址P,然后再将新结点的指针域改成NULL。
2、
头插入:新建一个结点他的指针域指向头结点的头地址,修改之后在将链表的头地址指向新建结点的地址这样新建结点就成为链表新的头节点
**
分析:
1,只要是修改头指针则必须传递头指针的地址,否则传递头指针值即可(即头指针本身)。这与普通变量类似,当需要修改普通变量的值,需传递其地址,否则传递普通变量的值即可(即这个变量的拷贝)。使用二级指针,很方便就修改了传入的结点一级指针的值。 如果用一级指针,则只能通过指针修改指针所指内容,却无法修改指针的值,也就是指针所指的内存块。所以创建链表和销毁链表需要二级指针或者一级指针引用。
2,不需要修改头指针的地方用一级指针就可以了,比如插入,删除,遍历,清空结点。假如头指针是L,则对L->next 及之后的结点指针只需要传递一级指针。
3,比如一个结点p,在函数里要修改p的指向就要用二级指针,如果只是修改p的next指向则用一级指针就可以了
**
写头插时应该先想好代码的思路:
//创建结点元素
//创建空链表
//创建结点
//插入节点函数
//编译链表
#include
#include
struct node //构思结点元素
{
int num;
char name[20];
int age;
struct node * next;
};
typedef struct node Node;
typedef Node * Link;
void creat_list(Link * head) //创建链表初始化为空表
{
*head = NULL;
}
void is_malloc_ok(Link new_node) //判断创建的结点是否成功分配了空间
{
if(NULL == new_node)
{
printf("malloc error!\n");
exit(-1);
}
}
void creat_new_node(Link * new_node)
{
*new_node = (Link)malloc(sizeof(Node));
is_malloc_ok(*new_node);
}
void display(Link head) //打印链表
{
Link p;
p = head;
if(NULL == head)
{
printf("Link is empty!\n");
return;
}
while(NULL != p)
{
printf("%d\n",p->num);
p = p->next;
}
}
void insert_node_head(Link * head,Link new_node)
{
new_node->next = *head; //通过图2进行理解这里的地址转换
*head = new_node;
}
int main()
{
int i = 0;
Link head = NULL; //初始化避野指针
Link new_node = NULL;
creat_list(&head); //**head是头地址,对于链表创建是对于地址的操作,
因为链表名本身代表链表首地址,因此在调用函数操作传入一个二阶地址,
通过解引用(*head)来操作指针head**
for(i=0; i < 10; i++)
{
creat_new_node(&new_node);
new_node->num = i + 1;
insert_node_head(&head,new_node);
}
display(head);
return 0;
}
void insert_node_tail(Link * head,Link new_node)
{
Link p; //尾插需要找到上一个结点的尾指针,因此需要一个变量定义此指针
p = *head;
if(NULL == *head) //链表为空时先插入第一个结点
{
*head = new_node;
new_node->next = NULL;
}
else //当P不为空时插入第二个结点
{
while(NULL != p->next)
{
p = p->next;
}
p->next = new_node; //尾插让新结点的next都是NULL,头指向上一个结点
new_node->next = NULL;
}
由上面的例子以及比较,我们可以看见:
1、头插法相对简便,但插入的数据与插入的顺序相反;
2、尾插法操作相对复杂,但插入的数据与插入顺序相同。
**
int insert_node_mid_sort(Link * head,Link new_node)
{
Link p,q;
p = q = *head;
if(NULL == *head) //空表时将第一个结点插入此时头结点地址不为空
{
*head = new_node;
new_node->next = NULL;
}
else if((*head)->num > new_node->num) //当结点的num>头结点时将他插入到头结点前面
{
new_node->next = *head;
*head = new_node;
}
else
{
while(p != NULL && p->num < new_node->num) //参考下图分析代码 (&&有一个不满足条件时就跳出代码执行下面的代码)
{
q = p;
p = p->next;
}
q->next = new_node;
new_node->next = p;
}
}
只需要将6的头插入到5的尾,6的尾巴插入到7的头就可以了。
while(p != NULL && p->num < new_node->num)
因为此时链表中已经有了结点,所以P不是NULL,切有num,此时5<6,
p,q,head,地址相同,此时p指向下一个结点
q = p;
p = p->next;
q->next = new_node;
new_node->next = p;
此时将new_node的头地址,尾地址互换插入到链表中去,
**
void insert_node_mid_front(Link * head,Link new_node,int loc)
{
Link p,q;
p = q = *head;
if((*head)->num == loc)
{
new_node->next = *head;
*head = new_node;
}
else
{
while(p != NULL && p->num != loc)
{
q = p;
p = p->next;
}
q->next = new_node;
new_node->next = p;
}
} //此代码原理同上只是新建一个loc
#if //在主函数中打印
printf("\n\n");
create_node(&new_node);
new_node->num = 100;
printf("please input loc:\n");
scanf("%d",&loc);
insert_node_mid_front(&head,new_node,loc);
display(head);
void delete_node(Link * head,int num_del)
{
Link p, q;
p = q = *head;
if(NULL == *head)
{
printf("Link is empty!\n");
return;
}
else if((*head)->num == num_del)
{
*head = (*head)->next ;
free(p);
return;
}
else
{
while(NULL != p && p->num != num_del)
{
q = p;
p = p->next;
}
if(NULL == p)
{
printf("no such node!\n");
}
else
{
q->next = p->next;
free(p);
}
}
}
Link search_node(Link head,int num_serch)
{
Link p;
p = head;
if(NULL == head)
{
printf("Link is empty!\n");
return NULL;
}
else
{
while( p != NULL && p->num != num_serch)
{
p = p->next;
}
if(NULL == p)
{
return NULL;
}
else
{
return p;
}
}
}
**
对于如何使用1级指针或者2级指针参考博客
待续…