带头结点的单链表

浅谈带头结点的单链表

1、头结点:在栈区开辟,指针域指向第一个首元结点,数据域不存储数据,可以存储当前结点的个数;
2、普通结点:无论是头结点还是普通结点都是一个结构体类型,由指针域和数据域组成;
指针域指向下一个结点,存储下一个结点的地址;
数据域可以设置成联合体类型,成员由数据元素和结点个数组成,之所以将数据域设置成联合体类型,是因为考虑到头结点数据域存储结点个数,普通结点数据域存储结点个数。
3、带头结点的单链表的基本操作

#define TRUE 1;
#define FALSE 0;
typedef int ElemType;
typedef union data//数据域:联合体类型,成员:数据元素,结点个数;
{
 ElemType val;
 int            num;
}Data;
typedef struct Node//结点
{
 Data data;//数据域:头结点存储当前结点的个数,头结点在栈区开辟,普通结点存储数据元素
 struct Node * next;//指针域:指向下一个结点,存储下一个结点的地址
}LNode, * LinkList;

static LinkList ApplyNode(ElemType val, LinkList next)//申请一个新结点;
{
 LinkList new_node = (LinkList)malloc(sizeof(LNode));
 assert(new_node != NULL);
 if (new_node == NULL)
 {
  return NULL;
 }
new_node->data.val = val;
 new_node->next = next;
 return new_node;
}

static LinkList Find_Pos_P(LinkList head,int pos)//找pos的前一个结点
{
 LinkList p = head;
 while(pos > 0)
 {
  p = p->next;
  pos--;
 }
 return p;
}

int Init_HeadLinklist(LinkList head)//初始化, next,data
{
 if (head == NULL)
 {
  exit(0);
 }
 head->next = NULL;
 head->data.num = 0;
 return TRUE;
}

int Insert_HeadLinkList_Pos(LinkList head,ElemType val,int pos)//按位置插入 o(n)
{
 if (head == NULL)
 {
  exit(0);
 }
 if (pos<0||pos>head->data.num)
 {
  return FALSE;
 }
 //找pos之前的结点
 /*LinkList p = head;
 while(pos > 0)
 {
  p = p->next;
  pos--;
 }*///写一个函数来代替找pos之前的结点
 LinkList p = Find_Pos_P(head,pos);
 LinkList new_node = ApplyNode(val,p->next);
 p->next = new_node;
 head->data.num++;
 return TRUE;
}

int Delete_HeadLinkList_Pos(LinkList head,int pos)//按位置删除 o(n)
{
 assert(head != NULL);
 if (head == NULL)
 {
  return FALSE;
 }
 if (pos<0||pos>=head->data.num)
 {
  return FALSE;
 }
 LinkList p = Find_Pos_P(head,pos);
 LinkList q = p->next;
 p->next = q->next;
 free(q);
 q = NULL;
 head->data.num--;
 return TRUE;
}

LinkList FindNode_Pos(LinkList head,int pos)//按位置查找 o(n)
{
 assert(head != NULL);
 if (head == NULL)
 {
  exit(0);
 }
 if (pos<0||pos>=head->data.num)
 {
  return NULL;
 }
 LinkList p = Find_Pos_P(head,pos);
 return p->next;
}

int  Clear_HeadLinkList(LinkList head)//清空
{
 assert(head != NULL);
 if (head == NULL)
 {
  exit(0);
 }
 while(head->data.num > 0)
 {
  if (!Delete_HeadLinkList_Head(head))
  {
   return FALSE;
  }
 }
 return TRUE;
}

int Destroy_HeadLinkList(LinkList head)//销毁
{
 return Clear_HeadLinkList(head);
}

4、带头结点的单链表的经典面试题
只讲思路和方法,代码自己实现

第一题:找倒数第k个结点(不知道结点的个数)
1.1、知道结点个数:假设已知结点个数为n,倒数第k个结点,就是正数的第n-k个结点;
1.2、不知道结点个数:设置两个结点指针p和q,当q走到结尾的时候,p刚好走到倒数第k个结点处;
p即为所求的结点,p和q相差的结点数就为k;
注意:k要判断合法性;

第二题:在时间复杂度为O(1)的情况下删除结点p(p不是尾结点)
2.1、删除结点p要找p的前一个结点,如果按照这种方法,时间复杂度为o(n),不符合要求;
2.2、删除一个结点其实是删除这个结点的数据,那么可以找结点p的直接后继q,将p的直接后继q中的数据复制给p
那么将q->next赋给p->next,在将q给删了,从而间接删除了p;
这就是为什么结点p不能是尾结点,因为尾结点的直接后继为NULL;

第三题:单链表的原地逆置
3.1、设置3个指针变量求解,s=NULL,p=head->next,q=p->next;
3.2、只设置两个指针变量,p=head->next,q=p->next,head->next=NULL,单链表的原地逆置就相当于头插;

第四题:判断两个单链表是否相交,找到相交的一个结点
解:y型结构:两个单链表相交;
设置两个指针变量p和q(p和q分别代表两条单链表);
p和q同时遍历一遍的长度之差就是他们相交之前的长度之差。
因此可以让长度较长的单链表先走一个差值,然后在同时遍历,p和q相等的地方就是相交点。

第五题:判断单链表是否有环,找到入环的第一个结点
解:第一步判断单链表是否有环:需要设置快慢指针,慢指针p一次走一个(p=p->next)
快指针q一次走两个(q=q->next->next),当p和q相等时说明单链表有环。
第二步找到入环的第一个结点:
x为从头到入环的第一个结点的路径,y为入环结点到pq相等处的路径,z为pq相等处向后走到入环结点的路径;
p的路径:x+y; q的路径:x+(y+z)n+y;
找关系:2(x+y)=x+(y+z)n+y->x+y=(y+z)n->x=(y+z)(n-1)循环路径+z->x=z;
即p从头开始走,q从pq相等处开始走,pq相等处即为入环结点。(注意此时的p和q都是一次走一个);

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值