数据结构——链表(面试必考)

在链表中,每个数据元素都配有一个指针,这意味着,链表上的每个“元素”都长下图这个样子:


数据域用来存储元素的值,指针域用来存放指针。数据结构中,通常将上图这样的整体称为结点。

在 C 语言中,可以用结构体表示链表中的结点,例如: 

struct node{

    char data; //代表数据域

    struct node * next; //代表指针域,指向直接后继元素

};

我们习惯将结点中的指针命名为 next,因此指针域又常称为“Next 域”。

头结点、头指针和首元结点

一个完整的链表应该由以下几部分构成:

头指针:一个和结点类型相同的指针,它的特点是:永远指向链表中的第一个结点。上文提到过,我们需要记录链表中第一个元素的存储位置,就是用头指针实现。

结点:链表中的节点又细分为头结点、首元结点和其它结点:

头结点:某些场景中,为了方便解决问题,会故意在链表的开头放置一个空结点,这样的结点就称为头结点。也就是说,头结点是位于链表开头、数据域为空(不利用)的结点。

首元结点:指的是链表开头第一个存有数据的结点。

其他节点:链表中其他的节点。
也就是说,一个完整的链表是由头指针和诸多个结点构成的。每个链表都必须有头指针,但头结点不是必须的。

例如,创建一个包含头结点的链表存储 {1,2,3},如下图所示:


再次强调,头指针永远指向链表中的第一个结点。换句话说,如果链表中包含头结点,那么头指针指向的是头结点,反之头指针指向首元结点。

1.单链表

(1)链表的创建,实现步骤如下:

定义一个头指针;

创建一个头结点或者首元结点,让头指针指向它;

每创建一个结点,都令其直接前驱结点的指针指向它。

#include <stdio.h>
struct node {

    char name[16];

    struct node *next;

};
struct node head;//这里是创建一个头结点 
struct node *pthead=&head;
struct node *pttail=&head;//创建头尾指针,你们可以不用,个人习惯 

void list_travel(struct node *pthead)
{
	struct node *p = pthead->next;
	while(p){
		printf("%s ",p->name);
		p=p->next;//指向下一个节点
}
		printf("\n");
}
int main()
{
	struct node *p;
	pthead->next=NULL;//创建头结点
    printf("please input name:");
	while(1)
	{
		p=malloc(sizeof(*p));
		scanf("%s",p->name);
		p->next=NULL;
		if(strcmp(p->name,"#")==0)
		{
			free(p);
			break;
		}
	// 在尾部添加节点 
	pttail->next = p;
	pttail = pttail->next; //p;
	 } 
	 //链表的遍历
	printf("链表为:: ");
	list_travel(pthead);
	 return 0;
 } 

(2)链表的插入

如下图,我们要在p的后面插入m,只需将m指向zheng,在将p指向m即可。

#include <stdio.h>
struct node {

    char name[16];

    struct node *next;

};
struct node head;//这里是创建一个头结点 
struct node *pthead=&head;
struct node *pttail=&head;//创建头尾指针,你们可以不用,这里是为了更好的帮助大家解 

	void list_travel(struct node *pthead)
	{
		struct node *p = pthead->next;
		while(p){
			printf("%s ",p->name);
			p=p->next;
	}
			printf("\n");
	}
	
int list_insert_node_after( struct node *pthead,char *findname, char *name )//头结点,要插入的位置,插入的数据 
	{
	struct node *p = pthead->next;
	while( p ){
	if( strcmp(p->name,findname) ==0 ){
	// p结点与要插入的位置相等
	struct node *m=malloc(sizeof(*m));
	strcpy(m->name,name); m->next=NULL;
	//在p后面插入m 
	m->next=p->next; p->next=m;
	return 0;
	}
	p=p->next;
	}
	/*here p==NULL*/
	return -1;
	}

int main()
{
	struct node *p;
	pthead->next=NULL;//创建头结点
    printf("please input name:");
	while(1)
	{
		p=malloc(sizeof(*p));
		scanf("%s",p->name);
		p->next=NULL;
		if(strcmp(p->name,"#")==0)
		{
			free(p);
			break;
		}
	// 在尾部添加节点 
	pttail->next = p;
	pttail = pttail->next; //p;
	 } 
	 //travel
	printf("链表为:: ");
	list_travel(pthead);
	
	//在节点后插入 
	list_insert_node_after(pthead,"wu","zhang");
	printf("插入后: ");
	list_travel(pthead);
	 return 0;
 } 

 (3)链表的删除

删除就是指针直接跳过档期啊节点,指向下一个节点就可以完成删除,具体如下图:删除节点zheng

实现代码:

#include <stdio.h>
struct node {

    char name[16];

    struct node *next;

};
struct node head;//这里是创建一个头结点 
struct node *pthead=&head;
struct node *pttail=&head;//创建头尾指针,你们可以不用,这里是为了更好的帮助大家解 

void list_travel(struct node *pthead)
	{
		struct node *p = pthead->next;
		while(p){
			printf("%s ",p->name);
			p=p->next;
	}
			printf("\n");
	}
	
int list_insert_node_after( struct node *pthead,char *findname, char *name )//头结点,要插入的位置,插入的数据 
	{
	struct node *p = pthead->next;
	while( p ){
	if( strcmp(p->name,findname) ==0 ){
	// p结点与要插入的位置相等
	struct node *m=malloc(sizeof(*m));
	strcpy(m->name,name); m->next=NULL;
	//在p后面插入m 
	m->next=p->next; p->next=m;
	return 0;
	}
	p=p->next;
	}
	/*here p==NULL*/
	return -1;
	}

int list_delete_node(struct node *pthead, char *name)
	{
	struct node *pre = pthead;
	struct node *p = pthead->next;
	while(p){
	if(strcmp(p->name,name)==0){
	//找到要删除的结点 
	pre->next=p->next; free(p);
	return 0;
	}
	p=p->next;
	 pre = pre->next;
	}
	return -1;
	}
	
int main()
{
	struct node *p;
	pthead->next=NULL;//创建头结点
	printf("please input name:");
	while(1)
	{
		p=malloc(sizeof(*p));
		scanf("%s",p->name);
		p->next=NULL;
		if(strcmp(p->name,"#")==0)
		{
			free(p);
			break;
		}
	// 在尾部添加节点 
	pttail->next = p;
	pttail = pttail->next; //p;
	 } 
	 //travel
	printf("链表为:: ");
	list_travel(pthead);
	
	//在节点后插入 
	list_insert_node_after(pthead,"wu","zhang");
	printf("插入后: ");
	list_travel(pthead);
	
	//删除 
	list_delete_node(pthead,"zheng");
	printf("删除后 : ");
	list_travel(pthead);
	 return 0;
 } 

2.双向链表

双向链表与单向链表比较就是多了一个直接前驱,可以进行双向操作,可进可退。下面给大家举个例子:和上面单链表一致,创建zhou wu zheng wang双向链表实现创建、插入和删除。

(1)创建。上一个节点的后继指向下一个节点的前驱,形成一个环。

//双链表的创建
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct dnode {
    char name[16];
    struct dnode *next,*pre;
};
 
struct dnode head;
struct dnode *pthead=&head,*pttail=&head;
//头尾指针,双向操作
 
void dlist_travel(struct dnode *pthead)
{
    struct dnode *p = pthead->next;
    printf("从头遍历:");
    while(p){
        printf("%s ",p->name);
        p=p->next;
}
    printf("\n");
    printf("从尾部遍历:");
    p=pttail;
while(p!=pthead){
    printf("%s ",p->name);
    p=p->pre;
}
    printf("\n");
}
 
void main()
{
    //创建头节点  
     printf("请输入name,以#号结束:");
while(1){
    struct dnode *p = malloc(sizeof(*p));
    p->pre = p->next =  NULL;
    scanf("%s",p->name);
    if(strcmp(p->name,"#") ==0){
        free(p);
        break;
}
    //链表的创建,增加节点 
    p->pre = pttail;
    pttail->next = p;
    pttail=pttail->next;
}
    //遍历
    dlist_travel(pthead);

}

(2)插入:我们直接看图,要在n和m的中间插入有一个节点p,n的后继指向p的前驱,p的前驱指向节点n,p的后继又指向节点m,m的前驱又指向节点p,形成一个环,就可以完成节点的插入,相信大家一看图就明白了。

//双链表的插入
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct dnode {
    char name[16];
    struct dnode *next,*pre;
};
 
struct dnode head;
struct dnode *pthead=&head,*pttail=&head;
//头尾指针,双向操作
 
void dlist_travel(struct dnode *pthead)
{
    struct dnode *p = pthead->next;
    printf("从头遍历:");
    while(p){
        printf("%s ",p->name);
        p=p->next;
}
    printf("\n");
    printf("从尾部遍历:");
    p=pttail;
while(p!=pthead){
    printf("%s ",p->name);
    p=p->pre;
}
    printf("\n");
}
 
int dlist_insert_before(struct dnode *pthead, char *findname, char *name)
{

    struct dnode *m=pthead->next;
while( m ){
    if(strcmp(m->name,findname) ==0 ){
    struct dnode *n = m->pre;
    struct dnode *p = malloc(sizeof(*p));
    strcpy(p->name,name);
//插入 
    n->next = p; p->pre = n;
    p->next = m; m->pre = p;
    return 0;
}
    m=m->next;
}
    return -1;
}
 
void main()
{
    //创建头节点  
     printf("请输入name,以#号结束:");
while(1){
    struct dnode *p = malloc(sizeof(*p));
    p->pre = p->next =  NULL;
    scanf("%s",p->name);
    if(strcmp(p->name,"#") ==0){
        free(p);
        break;
}
    //链表的创建,增加节点 
    p->pre = pttail;
    pttail->next = p;
    pttail=pttail->next;
}
    //遍历
    dlist_travel(pthead);
    
    //在后面插入
    dlist_insert_before(pthead, "zheng", "sun");
    printf("插入后:\n");
    dlist_travel(pthead);
    
   
}

(3)删除:如图,删除节点p,m的next域指向n,n的前驱指向m形成一个环就可以完成双链表的删除操作。

//双链表的删除
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct dnode {
    char name[16];
    struct dnode *next,*pre;
};
 
struct dnode head;
struct dnode *pthead=&head,*pttail=&head;
//头尾指针,双向操作
 
void dlist_travel(struct dnode *pthead)
{
    struct dnode *p = pthead->next;
    printf("从头遍历:");
    while(p){
        printf("%s ",p->name);
        p=p->next;
}
    printf("\n");
    printf("从尾部遍历:");
    p=pttail;
while(p!=pthead){
    printf("%s ",p->name);
    p=p->pre;
}
    printf("\n");
}
 
int dlist_insert_before(struct dnode *pthead, char *findname, char *name)
{

    struct dnode *m=pthead->next;
while( m ){
    if(strcmp(m->name,findname) ==0 ){
    struct dnode *n = m->pre;
    struct dnode *p = malloc(sizeof(*p));
    strcpy(p->name,name);
//插入 
    n->next = p; p->pre = n;
    p->next = m; m->pre = p;
    return 0;
}
    m=m->next;
}
    return -1;
}
 
int dlist_delete(struct dnode *pthead,char *name)
{
    struct dnode *p = pthead->next;
   
while(p){
    if(strcmp(p->name,name)==0){
    //删除 
    struct dnode *m=p->pre;
    struct dnode *n=p->next;
    m->next=n;
    n->pre=m;
    free(p);
    return 0;
}
    p=p->next;
}
    return -1;
}
 
void main()
{
    //创建头节点  
     printf("请输入name,以#号结束:");
while(1){
    struct dnode *p = malloc(sizeof(*p));
    p->pre = p->next =  NULL;
    scanf("%s",p->name);
    if(strcmp(p->name,"#") ==0){
        free(p);
        break;
}
    //链表的创建,增加节点 
    p->pre = pttail;
    pttail->next = p;
    pttail=pttail->next;
}
    //遍历
    dlist_travel(pthead);
    
    //在后面插入
    dlist_insert_before(pthead, "zheng", "sun");
    printf("插入后:\n");
    dlist_travel(pthead);
    
    //删除 
    dlist_delete(pthead,"zheng");
    printf("删除后:\n");
    dlist_travel(pthead);
}
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

春风从不入睡、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值