算法通关村第一关——链表(青铜 C语言)

单链表

理解C语言里是如何构造出链表的(初始化代码可以补基础)

口述过程:

链表是一种数据结构,由一个头结点,头指针和多个结点构成。

结点的数据类型:。

一般来说,一个结点包含数据域和指针域,分别用来存储数据和指出上下文。

在这里插入图片描述
结点和链表初始化代码:

//自定义
typedef struct linkedlist{

		ElementType e;				//数据域
		
		struct linkedlist * next;	//指针域

};

//Leetcode 定义一般如下
struct ListNode{
		int val; 					//data
		struct ListNode *next; 		//pointer
}

有了结点类型,距离创建一个普通单链表,还需要头指针和头结点
并且在初始化函数中,我们需要返回一个指向新链表的头指针。
因此是指针函数。参数为空
初始化函数代码如下:

struct ListNode * initLink()
{
	int i=0;
	struct ListNode * p = null;
	struct ListNode * temp = (struct ListNode *)malloc(sizeof(struct ListNode) )
	p = temp ;
	for(i = 1 ; i < 5 ; i++)
	{
	struct ListNode * a = (struct ListNode *)malloc(sizeof(struct ListNode) );
	a->val= i;
	a->next = null;
	temp->next = a;
	temp = temp->next;
	}
/*
这一段循环代码详解:
首先,为了构造链表,我们需要在每一次循环中创建一个新结点,并且给它赋值,但是如果不创建一个结点指针指向一块分配好的内存空间,新节点就会在一次循环完成后就会丢失。所以为什么要建立结点指针就是这个原因,而指向头结点的指针temp相对于循环是一个全局变量,因此我们可以用temp进行遍历,而真正的指向头结点的指针head不动,用来返回整个链表。因为只要有指针,我们就能找到这样逻辑连续的链表空间,遍历中改变temp指针的指向并不会对头指针造成影响
*/
	return p;
}

增加元素

首部添加元素

口述过程:
创建一个结点,新结点通过头指针指向头结点,头指针指向新结点
结果:返回头指针

中间添加元素

口述过程:
首先,通过一个指针遍历到当前位置
在这个位置中,我们需要通过插入位置上一个结点找到下一个结点,便于添加新结点指针域,所以我们遍历到上一个结点可以停止。
此时,我们将新节点指向下一个节点,然后再把前驱节点指向该节点
结果:返回头指针

尾部添加元素

口述过程:
尾部添加元素很简单,将最后一个结点后驱指向新节点,然后新节点指null(如果没有指的话)
结果:返回头指针

单链表增加元素操作代码汇总(以顺序链表为例)

struct ListNode * insertNode(struct ListNode * head,struct ListNode * nodeInsert ,int position)
{
 //首部添加结点
 int size = getLength(head);
 if(position>size+1 || position < 1)
 return head;
 
 if(head==null)
 return nodeInsert;
 
 if(position==1)
 { 
 nodeInsert->next = head;
   head = nodeInsert;
   return head;
 }
  
  //中间添加结点
  int count =1;
  struct ListNode * p = head;
  while ( count < position-1 )
  {
  p = p->next;
  count++;
  }
 nodeInsert->next = p->next;
  p->next = nodeInsert;
  
  return head;
  
 }

删除结点

删除头结点

口述过程:将头指针指向头结点下一个节点,释放头结点。
结果:返回头指针

删除最后一个节点

口述过程:将倒数第二个结点指向空。
结果:返回头指针

删除中间节点

口述过程:将上一个节点指向中间结点的下一个节点,然后,将中间节点释放
结果:返回头指针

代码汇总

struct ListNode * deletNode (struct ListNode * head,int position)
{
 ## delet head node
 int size = getLength(head);
 
 if(head==null)
 return NULL;

 if(position > size || position < 1 )
 {
 return head;
 }
 
 if(postion == 1)
 { struct ListNode * curNode = head;
   head = head->next;
   free(curNode);
   return head;
   }else{
   int count =1;
   struct ListNode * curNode = head;
   while(count<postion -1)
   {
   curNode = curNode->next;
   count++;
   }
   struct ListNode *mid = curNode->next;
   curNode->next=curNode->next->next;
   //新建mid指针,指向中间节点,不然释放不了
   
   free(mid);
   return head;
   }
  }
 


遍历单链表

口述过程:新建头指针,循环结束条件:p->next==null | p

!=null
结果:打印链表中的值或者获得链表长度

PS:就算不需要返回头指针,也要注意头指针归零或者直接用虚拟指针遍历链表

代码:

void  printList(struct ListNode * head)
{
 struct ListNode * p =head;
 while(p)
 {
 printf("%d ",p->val);
 printf(",");
 p=p->next
 }
}

int getLinkLength(struct ListNode * head)
{
	 int length = 0 ;
	 struct ListNode * p = head;
	 
	 while(p)
	 {
	 length++;
	 p=p->next;
	 )
	 return length;
	 
 }
 

双向链表(部分过程简化)

构建

口述过程:双向链表结构很简单,多了一个前驱节点。
那么在初始化操作中先找后继在找前驱。

代码:

typedef struct listNode{
		ElementType e;
		struct listNode * up;
		struct listNode * down;
		}ListNode;
		
struct ListNode * initdLink()
{
 int i;
 //创建头指针
 struct ListNode * head = null;
 //创建头结点
 struct ListNode * temp = (struct ListNode *)malloc(sizeof(struct ListNode));
	
	temp->e = 0;
	temp->up=null;
	temp->down =null;
	p=temp;
	for(i =0; i< 6 ;i++)
	{
	struct ListNode * q = (struct ListNode *)malloc(sizeof(struct ListNode));
	q->e = i;
	q->up = null;
	temp-> down = q;
	q->up =temp;
	temp=temp->down//q
	}
	return head
	}

增删

已知上一个节点位置,该结点位置。

增加一个节点:

口述过程:双向链表由于有前驱和后继,因此我们需要有一定顺序。

比如:先解决后继,再解决前驱。该节点后继指向上一个结点的后继,上一个节点后继指向该节点,该节点的后继指向该节点,该节点的前驱指向上一个节点

代码:

struct ListNode * insertNode(struct ListNode * head,struct ListNode * nodeInsert ,int position)
{
 //首部添加结点
 int size = getLength(head);
 if(position>size+1 || position < 1)
 return head;
 
 if(head==null)
 return nodeInsert;
 
 if(position==1)
 { 
	 nodeInsert->next = head;
 	 head->cur=nodeInsert
   head = nodeInsert;
   return head;
 }
  
  //中间添加结点
  int count =1;
  struct ListNode * p = head;
  while ( count < position-1 )
  {
  p = p->next;
  count++;
  }
  
  nodeInsert->next = p->next;
  nodeInsert->cur=p;
  nodeInsert->next->cur=nodeInsert;
  
  return head;
  
 }
struct ListNode * deletNode (struct ListNode * head,int position)
{
 ## delet head node
 int size = getLength(head);
 
 if(head==null)
 return NULL;

 if(position > size || position < 1 )
 {
 return head;
 }
 
 if(postion == 1)
 { 
 	 struct ListNode * curNode = head;
   head = head->next;
   head->next->cur=head;
   free(curNode);
   return head;
   }else{
   int count =1;
   struct ListNode * curNode = head;
   while(count<postion -1)
   {
   curNode = curNode->next;
   count++;
   }
   struct ListNode *mid = curNode->next;
   curNode->next=curNode->next->next;
   mid->next->cur = curNode;
   //新建mid指针,指向中间节点,不然释放不了
 
   free(mid);
   return head;
   }
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

~Yogi

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

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

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

打赏作者

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

抵扣说明:

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

余额充值