王道第二章链表相关oj题及之后的练习(详解)

1.设计一个递归算法,删除不带头结点的单链表L中所有值为X的结点

ListNode* Del_x(ListNode* L,int x)
{
	if(L == NULL)
    {
		return NULL;
    }
  	ListNode* res = Del_x(L->next,x);
  	//如果该头指针的值为x,那么将下一层返回过来的节点返回给本层的上一层
  if(L->val == x)
  {
	return res;
  }
  else//如果值不为x,则将上一层的节点,链接到本层,然后将本层的节点返回给上一层
  {
    L->next = res;
    return L;
  }
}

递归展开图:

e4d6b76e350241fdbb59ac966b157a2f.png

 相关题目:
力扣https://leetcode.cn/problems/remove-linked-list-elements/2.在带头结点的单链表中,删除所有值为x的结点,并释放其空间,假设值为x的结点不唯一,试编写算法以实现上述操作

06eca8c2023d4c7c837aff0e0cb57cb1.png

思路:

设置三个指针,cur用来指向当前结点,prev用来指向cur的前一个结点,next用来指向cur的后一个结点,当cur == NULL时,链表删除完成

void Del_x2(ListNode* L,int x)
{
  if(L->next == NULL)  
  {
	return L;
  }
 	ListNode* prev = L;
	ListNode* cur = L->next;
  while(cur)
  {
    next = cur->next;
	if(cur->val == x)
    {
      perv->next = next;
      free(cur);
    }
    else
    {
		prev = cur;
    }
      	cur = next;
  }
}

 3.设L为带头节点的单链表,编写算法实现从尾到头反向输出每个结点的值

4a683421674e4d4dab815fcc71343f5f.png

void reserve_output(listNode* L)
{
  //带有头结点
  //如果下一个不为空,就一直向后找
	if(L->next != NULL)
    {
		reserve_output(L->next);
    }
  //此时L->next == NULL
  //打印节点
  if(L != NULL)
  {
	 printf("%d",L->data);
  }
}

相关练习:

力扣https://leetcode.cn/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof/submissions/4.试编写在带头节点的单链表L中删除一个最小值结点的高效算法(假设最小值结点是唯一的)

0b9e468e977e4849a92bff6d00a6c792.png

//方法一:先找到最小值的结点,再进行删除
void Del_min(SListNode& L)
{
	//先找到最小结点
  SListNode* cur = L->next;
  int min = L->next->val;//假如最小节点是首元结点
  while(cur->next != NULL)
  {
    cur = cur->next;
    if(cur->data < min)
    {
      min = cur->data;
    }
  }
  //找到最小值,进行删除结点
    SListNode* prev = NULL;
    cur = head->next;
    if(cur->val == min)//如果首元结点
    {
        head->next = cur->next;
        free(cur);
        cur = NULL;
    }
    else//中间节点
    {
        while(cur->next->val != min)
        {
            cur = cur->next;
        }
        prev = cur;
        cur = cur->next;
        prev->next = cur->next;
        free(cur);
        cur = NULL;
    }
}
//方法二:
用俩个节点分别保存最小值节点的前驱和最小值新节点,再用俩个节点去分别找最小值结点,当链表找完,然后用保存的结点,将最小值结点删除

 相关练习:

力扣https://leetcode.cn/problems/shan-chu-lian-biao-de-jie-dian-lcof/submissions/

5.试编写算法将带头结点的单链表就地逆置,所谓"就地"是指辅助空间复杂度为o(1)

12067c2dad85489897ff2e2f8868ae9a.png

ListNode* Reserve(ListNode* L)
{
	ListNode* prev = NULL;
  	ListNode* cur = L->next;
 	ListNode* next = NULL;
  while(cur)
  {
    if(L->next == cur)
    {
		L->next = prev;
    }
	next = cur->next;
    cur->next = prev;
    prev = cur;
    cur = next;
  }
  L->next = prev;
  return L;
}

 相关问题:
力扣https://leetcode.cn/problems/fan-zhuan-lian-biao-lcof/

6.有一个带头节点的单链表L,设计一个算法使其元素递增有序

力扣https://leetcode.cn/problems/7WHec2/submissions/

//方法二:插入算法 但是会超时
struct ListNode* sortList(struct ListNode* head)
{
    //1.设置一个cur指针用于指向head给出的链表
    //2.再设置一个pre和next用于寻找给出的节点每次应该插入在什么位置
    struct ListNode* cur = head;
    struct ListNode* phead = NULL;//新建立的结点
       //第一次进行插入新节点
     if(cur)
     {
         phead = cur;
         cur = cur->next;
         phead->next = NULL;
     }
    while(cur)
    {
        struct ListNode* pre = NULL;
        struct ListNode* Next = phead;
        //如果开始第一个小于cur->data,说明要往后找
       if(Next->val < cur->val)
       {
            //进行中间插结点或者尾部插结点
            while( Next != NULL && Next->val < cur->val)
            {
                pre = Next;
                Next = Next->next;
            }
            if(Next)
            {
                //找到了> cur->data的节点
                pre->next = cur;
				cur = cur->next;
				pre = pre->next;
				pre->next = Next;
            }
            //Next为空
            else
            {   
                pre->next = cur;
                pre = cur;
                cur = cur->next;
                pre ->next = NULL;
            }
       }
       //头插
       else
       {
           phead = cur;
           phead->next = Next;
           cur = cur->next;
       }
    }
    return phead;

}

 7.设在一个带表头节点的单链表中所有元素节点的数据值无序,试编写一个函数,删除表中所有介于给定的俩个值(作为函数参数给出)之间的元素的元素(若存在)

c784d226934e4eaeadfa3354b968aca6.png

void DelBet(ListNode& L,int x,int y)
{
	ListNode* pre,*cur;
  	pre = L;
  	cur = L->next;
  	while(cur)
    {
		if(cur->data > i && cur->data < j )
        {
			pre->next = cur->next;
          	free(cur);
          	cur =pre->next;
        }
      else
      {
		pre = cur;
        cur = cur->next;
      }
    }
}

8.给定俩个单链表,编写算法找出俩个链表的公共节点

力扣https://leetcode.cn/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof/submissions/9.给定一个带表头结点的单链表,设head为头指针,结点结构为(data,next),data为整形元素,next为指针,试写出算法:按递增次序输出单链表中各个结点的数据元素,并释放节点所占存储空间(要求:不允许使用数组作为辅助空间)

void Print(ListNode& head)
{
	//1.用以指针每次去找最小的,并且设置一个它的prev指针
  while(head->next != NULL)
  {
    ListNode* prev = NULL;
  	ListNode* cur = head->next;
    //假设第一个是最小额
    int Min = cur->data;
    while(cur != NULL)
    {
      //找最小的
      if(cur->data < Min)
      {
		Min = cur->data;
      }
      prev = cur;
      cur = cur->next;
    }
    //找到了
    printf("%d",Min);
    prev->next = cur->next;
    free(cur);
  }
  free(head);
}

10.将一个带头节点的单链表A分解为俩个带头节点的单链表A和B,使得A表中含有原表中序号为奇数的元素,而表中含有原表中序号为偶数的元素,且保持其相对顺序不变

力扣https://leetcode.cn/problems/odd-even-linked-list/submissions/

11.设C={a1,b1,a2,b2,..,an,bn}为线性表,采用带有节点的hc单链表存放,设计一个就地算法,将其拆分为俩个线性表,使得A={a1,a2,...,an},B = {bn,..,b2,b1}.

97f0d22fd68a47dfb5d4753d0e0362e4.png

//就地逆置
ListNode* Reserve(ListNode* L)
{
	ListNode* prev = NULL;
  	ListNode* cur = L->next;
 	ListNode* next = NULL;
  while(cur)
  {
    if(L->next == cur)
    {
		L->next = prev;
    }
	next = cur->next;
    cur->next = prev;
    prev = cur;
    cur = next;
  }
  L->next = prev;
  return L;
}
void SplitList(ListNode& hc)
{
  //1.将A和B分别拿出来按奇偶性,然后将B就地逆置则可以分别得到A和B
	ListNode* A = hc->next;
  	ListNode* curA = hc->next;
 	ListNode* B = hc->next->next;
  	ListNode* curB = hc->next->next;
  	while(curA && curA->next && curB &&curB->next)
    {
      curA->next = curB->next;
      curA = curA->next;
      curB->next = curA->next;
      curB = curB->next;
    }
  //此时得到的是A{a1..an},B{b1...bn}
  //再将B逆置
  Reserve(B);
  //此时B变为{bn...b1}
 
}

12.在一个递增有序的线性表中,有数值相同的元素存在。若存储方式为单链表,设计算法去掉数值相,同的元素,是表中不再有重复元素.例如{7,10,10,21,30,42,42,42,51,70}将变为{7,10,21,30,42,51,70}

力扣https://leetcode.cn/problems/remove-duplicates-from-sorted-list/submissions/

方法一:暴力求解
struct ListNode* deleteDuplicates(struct ListNode* head)
{
    //1.设置一个指针指向当前结点,另一个指针去寻找有没有与当前元素相同的,有则删除,此时还有一个用于寻找指针的前一个节点
    if(head == NULL)
    {
        return NULL;
    }
    struct ListNode* cur = head;
    struct ListNode* Next = cur->next;
    struct ListNode* prev = head;
    while(cur)
    {
        while(Next)
        {
            if(Next->val == cur->val)
            {
                prev->next = Next->next;
                free(Next);
                Next = prev->next; 
            }
            else
            {
                prev = Next;
                Next = Next->next;
            }
        }
        cur = cur->next;
        //重置,将prev和Next,将新的cur当做表头结点
        prev = cur;
        if(cur)//如果cur不为空,重置Next才有意义
        {
          Next = cur->next;
        }
    }
    return head;
}
//方法二:
struct ListNode* deleteDuplicates(struct ListNode* head)
{
    //有序,那么相同值时相邻的,则用当前指针的后继指针去找是否与当前一样,一样删除,向后迭代,时间复杂度为o(n)
    if(head == NULL)
    {
        return NULL;
    }
    struct ListNode* cur = head;
    struct ListNode* Next = cur->next;
    while(Next)
    {
        if(Next->val == cur->val)
        {
            cur->next = Next->next;
            free(Next);
            Next = cur->next;
        }
        else
        {
            cur = Next;
            Next = Next->next;
        }
    }
    return head;
}

13.假设有俩个按元素值递增次序排列的线性表,均以单链表形式存储。请编写算法将这俩个单链表归并为一个按元素值递减次序排列的单链表,并要求利用原来俩个单链表的结点存放归并后的单链表。

26b0161cad7845f4a2b13214f436f639.png

void mergeTwoLists(ListNode* list1, ListNode* list2)
{
	//1.因为俩个链表都是升序的,比较这俩个链表,数值小的节点拿到新的节点处进行头插,
  //最后就变成了降序,有一个节点结束,那么另一节点直接都拿到新节点处进行头插即可
  //2.如果带头结点就减少一次判断,可以采取

  //建立头结点
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	newnode->next = NULL;
	ListNode* cur1 = list1;//用于保存list1的后一个节点
	ListNode* cur2 = list2;//用于保存list2的后一个节点

  //比较俩个链表中的节点,小的拿到NewNode中头插
  //结束条件,俩个链表都没到空
	while (list1 != NULL && list2 != NULL)
	{
		if (list1->data < list2 -> data)
		{
			cur1 = cur1->next;
			list1->next = newnode->next;
			newnode->next = list1;
			list1 = cur1;

		}
		else
		{
			cur2 = cur2->next;
			list2->next = newnode->next;
			newnode->next = list2;
			list2 = cur2;
		}
	}
	//当一个节点结束了
	if (list2 == NULL)
	{
		//list1拿到newnode处进行头插
		while (list1 != NULL)
		{
			cur1 = cur1->next;
			list1 = newnode->next;
			newnode->next = list1;
			list1 = cur1;
		}
	}
	else
	{
		while (list2 != NULL)
		{
			cur2 = cur2->next;
			list2 = newnode->next;
			newnode->next = list2;
			list2 = cur2;
		}
	}
	free(newnode);
}
int main()
{
		/*testSList();*/
		ListNode* A = (ListNode*)malloc(sizeof(ListNode));
		ListNode* B = (ListNode*)malloc(sizeof(ListNode));
		ListNode* C = (ListNode*)malloc(sizeof(ListNode));
		ListNode* D = (ListNode*)malloc(sizeof(ListNode));
		ListNode* E = (ListNode*)malloc(sizeof(ListNode));
		A->data = 1;
		B->data = 2;
		C->data = 4;
		//D->data = 4;
		//E->data = 5;
		A->next = B;
		B->next = C;
		C->next = NULL;
		//D->next = E;
		//E->next = NULL;
		ListNode* H = (ListNode*)malloc(sizeof(ListNode));
		ListNode* I = (ListNode*)malloc(sizeof(ListNode));
		ListNode* J = (ListNode*)malloc(sizeof(ListNode));
	
		H->data = 1;
		I->data = 3;
		J->data = 4;
		H->next = I;
		I->next = J;
		J->next = NULL;

		mergeTwoLists(A,H);
		
}

 e73cf9d0c4c742b3ae9c14e527d44661.png

14.设A和B是俩个单链表(带头结点),其中元素递增有序。设计一个算法从A和B中的公共元素产生单链表C,要求不破坏A、B的结点

d200132b053844ce83e213eae3911767.png

ListNode* BuyNode(int x)
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
void LookPubEle(ListNode* list1, ListNode* list2)
{
	//把俩个链表进行比较,小的那个节点指向下一个,再与另一个链表进行比较,如果还小继续,直到为空
  //如果俩个链表的节点值相等,则将可以复制给一个新节点,然后插入到newnode的中
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	newnode->next = NULL;
	ListNode* cur1 = list1;
	ListNode* cur2 = list2;
	while (cur1 && cur2)
	{
		if (cur1->data < cur2->data)
		{
			cur1 = cur1->next;
		}
		else if (cur1->data == cur2->data)
		{
			ListNode* Node = BuyNode(cur1->data);
			//进行插入头插
			Node->next = newnode->next;
			newnode->next = Node;
			//相等的话,cur1和cur2都再走到下一个节点进行比较
			cur1 = cur1->next;
			cur2 = cur2->next;
		}
		else
		{
			cur2 = cur2->next;
		}
	}
	print(newnode->next);
}

int main()
{
		/*testSList();*/
		ListNode* A = (ListNode*)malloc(sizeof(ListNode));
		ListNode* B = (ListNode*)malloc(sizeof(ListNode));
		ListNode* C = (ListNode*)malloc(sizeof(ListNode));
		ListNode* D = (ListNode*)malloc(sizeof(ListNode));
		ListNode* E = (ListNode*)malloc(sizeof(ListNode));
		A->data = 1;
		B->data = 2;
		C->data = 4;
		//D->data = 4;
		//E->data = 5;
		A->next = B;
		B->next = C;
		C->next = NULL;
		//D->next = E;
		//E->next = NULL;
		ListNode* H = (ListNode*)malloc(sizeof(ListNode));
		ListNode* I = (ListNode*)malloc(sizeof(ListNode));
		ListNode* J = (ListNode*)malloc(sizeof(ListNode));
	
		H->data = 1;
		I->data = 3;
		J->data = 4;
		H->next = I;
		I->next = J;
		J->next = NULL;

		LookPubEle(A,H);
}

b6bd8e978d70412b9533f8b76552ef2e.png

 15.已知俩个链表A和B分别表示俩个集合,其元素递增排列。编制函数,求A与B的交集,并存放与A链表中

与14题相似

思路:在14题的基础上,让公共的数只出现一次

ListNode* BuyNode(int x)
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
void PubliCNode(ListNode* list1, ListNode* list2)
{
	ListNode* cur1 = list1;
	ListNode* cur2 = list2;
	list1 = NULL;
	ListNode* cur3 = list1;//用于寻找是否重复
	int flag = 1;
	while (cur1 && cur2)
	{
		if (cur1->data < cur2->data)
		{
			cur1 = cur1->next;
		}
		else if (cur1->data == cur2->data)
		{
			while (cur3 && (cur1->data != cur3->data))
			{
				cur3 = cur3->next;
			}
			if (cur3)
			{
				//说明list1中有这个数了
				flag = 0;
			}

			if (flag)//flag == 1时进行插入,等于0时说明有这个数了,走到下一个节点
			{
				ListNode* Node = BuyNode(cur1->data);
				//不带头结点
				if (list1 == NULL)
				{
					list1 = Node;
				}
				else
				{
					Node->next = list1->next;
					list1->next = Node;
				}
				
			}
			cur3 = list1;//重置cur3
			flag = 1;//重置flag
			cur1 = cur1->next;
			cur2 = cur2->next;
		}
		else
		{
			cur2 = cur2->next;
		}
	}
	print(list1);
}

int main()
{
		/*testSList();*/
		ListNode* A = (ListNode*)malloc(sizeof(ListNode));
		ListNode* B = (ListNode*)malloc(sizeof(ListNode));
		ListNode* C = (ListNode*)malloc(sizeof(ListNode));
		ListNode* D = (ListNode*)malloc(sizeof(ListNode));
		ListNode* E = (ListNode*)malloc(sizeof(ListNode));
		A->data = 1;
		B->data = 1;
		C->data = 4;
		//D->data = 4;
		//E->data = 5;
		A->next = B;
		B->next = C;
		C->next = NULL;
		//D->next = E;
		//E->next = NULL;
		ListNode* H = (ListNode*)malloc(sizeof(ListNode));
		ListNode* I = (ListNode*)malloc(sizeof(ListNode));
		ListNode* J = (ListNode*)malloc(sizeof(ListNode));
	
		H->data = 1;
		I->data = 1;
		J->data = 4;
		H->next = I;
		I->next = J;
		J->next = NULL;

		/*mergeTwoLists(A,H);*/
		PubliCNode(A,H);
}

16.俩个整数序列A = a1,a2,...,am和B = b1,b2,...,bn已经存入俩个单链表中,设计一个算法,判断序列B是否是序列A的连续子序列

672a463221f84852bc44ce1f986abf8c.png

int  Pattern(ListNode* list1,ListNode* list2)
{
	//拿B的第一个元素和A的元素相比,如果相等,则A和B都指向下一个,然后再比较,
	//如果B中和A中的元素不相等,则B从第一个元素重新比较,B为空,则表示为子序列,B不为空,则表示不为子序列
	ListNode* head2 = list2;
	while (list1 && list2)
	{
		//相等
		if (list1->data == list2->data)
		{
			list1 = list1->next;
			list2 = list2->next;
		}
		else
		{
			//如果list2的头都相等,直接list1++
			if (list2 == head2)
			{
				list1 = list1->next;
			}
			//如果头相等,那么lits2重置,重新头再和list1去比较
			else
			{
				list2 = head2;
			}
		}
	}
	//如果list2没有指向空,则表示不是子序列,子序应该是list2指向空
	if (list2 == NULL)
	{
		return 1;
	}
	return 0;
}

3d34e181402e42378ebf76474938936d.png

 17.设计一个算法用于判断带头节点的循环双链表是否对称

51c33d01b6a34ac38496c747a89ea139.png

int summetry(ListNode* list1)
{
	//一个指针向右,一个指针向左,偶数个节点时,如果指向相邻结束
	//奇数个节点时,指向同一个结束,如果所有值都相等,则表示对称
	//优化,如果有一个不相等,直接不对称
	ListNode* ToLeft = list1->prev;
	ListNode* Toright = list1->next;
  //偶数个的结束条件是相邻结束,奇数个是指向同一个结束
	while (ToLeft != Toright && ToLeft->next != Toright)
	{
		if(Toleft->data == Toright->data)
        {
			Toleft = Toleft->next;
          	 Toright = Toright->prev;
        }
      	else
        {
          	//不相等就是不对称
          	return 0;
        }
	}
  return 1;
}

18.有俩个循环单链表,链表头指针分别为h1和h2,编写一个函数将链表h2链接到链表h1之后,要求链接h1之后,要求链接后的链表仍保持循环链表形式

f516bb7611ad4a5fbc72c54c84fed8dc.png

ListNode* Link(ListNode* list1,ListNode* list2)
{
	//找到list1 的尾,和list2的尾,然后进行连接
  	ListNode* cur1 = list1;
  	ListNode* cur2 = list2;
  	ListNode* tail1 = NULL;
    ListNode* tail2 = NULL;
  	while(cur1->next != list1)
    {
		cur1 = cur1->next;
    }
    tail1 = cur1;
  	while(cur2->next != list2)
    {
		cur2 = cur2->next;
    }
    tail2 = cur2;
  	tail1->next = list2;
  	tail->next = list1;
  	return list1;
}

19.设有一个带头节点的循环单链表,其结点值均为正整数.设计一个算法,反复找出单链表中结点值最小的结点并输出,然后将该结点从中删除,直到单链表空为止,再删除表头结点

89f841c865c84426a71ca3593a7d74ab.png

思路:min和prev初始值为第一个结点和头结点,如果cur寻找到比min里面的元素小的值,则让min和minprev指向,一轮之后,让cur和prev重置,因为是单循环链表,所以直到List1->next == List时,表示链表为空,然后删除头结点

void Del_MIn(ListNode* List1)
{
  ListNode* cur = List1->next;
  ListNode* prev = List1;
  ListNode* min = List1->next;
  ListNode* minprev = List1;
  while(List1->next != List1)
  {
       while(cur->next != List1)//找最小
       {
         //如果cur所指向的元素值小于min,那么让min和minprev指向对应
         if(cur->data < min->data)
         {
             min = cur;
             minprev = prev;
         }
         prev = cur;
         cur = cur->next;
        
       }
       printf("%d",min->data);
       minprev->next = min->next;
       free(min);
       cur = List1->next;
       prev = List1;
   	   min = List1->next;
       minprev = List1;
  }
   //删除头结点
    free(List1);
}

20.设头指针为L的带有表头结点的非循环双向链表,其每个结点中除有pred(前驱指针)、data(数据)和next(后继指针)域外,还有一个访问频度域freq。在链表被启用前,其值均初始化为零.每当在链表中进行一次Locate(L,x)运算时,令元素值为x的结点中freq域的值增加1,并使此链表中结点保持按访问频度非增(递减)的顺序排列,同时最近访问的结点排在频度相同的结点前面,以便使频繁访问的结点总是靠近表头。试编写符合上述要求的Locate(L,x)运算的算法,该运算为函数过程,返回找到结点的地址,类型为指针型。

7074cbe794d243128ba98f3e0383e704.png

DList* Locate(DList& L,int x)  
{
  //寻找这个x
  DList* cur = L->next;
  while(cur && cur->data != x)
  {
	cur = cur->next;
  }
  //可能链表为空没找到,也可能找到了
  if(cur == NULL)
  {
	printf("链表中无此结点\n");
    exit(0);
  }
  //找到了
  else 
  {
	//访问频度+1
    cur->freq += 1;
    //将该节点拿下来
    cur->pred->next = cur->next;
    cur->next->pred = cur->pred;
    //找他该放的地方
    //如果前面一直没有比他大的一直找到L结点了
    //和他相同的则将他放到相同的最前面
    DList* prev = cur->pred;//用于寻找前面的结点
    while(prev != L && prev->freq <= cur->freq)
    {
      prev = prev->pred;
    }
    //此时找到了比他访问频度大的或者找到了L结点
    prev->next->pred = cur;
    cur->next = prev->next;
    prev->next = cur;
    cur->pred = prev;
  }
  return cur;//返回x的结点指针
  
}

21.单链表有环,是指单链表的最后一个结点的指针指向了链表中的某个结点(通常单链表的最后一个结点的指针域是空的).试着编写算法判断单链表是否存在环。

(1)算法基本思想:

 快慢指针,如果有环,那么在环中,快指针与慢指针的距离每次都在缩进一步,迟早是会相等的
  如果没有环,他们是不会相遇的,如果结点是奇数个,fast->next == NULL,偶数个即是fast == NULL

(2)算法实现:

力扣https://leetcode.cn/problems/linked-list-cycle/

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) 
{
    //快慢指针,如果有环,那么在环中,快指针与慢指针的距离每次都在缩进一步,迟早是会相等的
    //如果没有环,他们是不会相遇的,如果结点是奇数个,fast->next == NULL,偶数个即是fast == NULL
    struct ListNode * fast = head;
    struct ListNode * slow = head;
    while(fast && fast->next)//没有环结束的条件
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)//有环迟早相遇
        {
            return true;
        }
    }    
    return false;
}

(3)时间复杂度:o(n)   空间复杂度:o(1)

(4)拓展:给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

力扣https://leetcode.cn/problems/linked-list-cycle-ii/

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) 
{
    //先判断是否有环,找到他们的相遇点,然后一个从头结点一个从相遇点一步一步走,迟早会在入口点相遇
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    struct ListNode* meet = NULL;

    while(fast && fast->next)
    {
        fast  = fast->next->next;
        slow = slow->next;
        if(slow == fast)
        {
            //相遇点
            meet = slow;
            break;
        }
    }
    if(fast == NULL || fast->next == NULL)//如果没有相遇点,&&的话如果前面为0,后面就不会看了
    {
        return NULL;
    }
    //俩个指针开始一步一步走
    struct ListNode* phead = head;
    while(phead != meet)
    {
        phead = phead->next;
        meet = meet->next;
    }
    return phead;
}

详细解释可以看此篇文章: 链表相关oj题 三_迷茫中的小伙的博客-CSDN博客

22.已知一个带有表头结点的单链表,假设该链表只给出了头指针list.在不改变链表的前提下,请设计一个尽可能高效的算法,查找链表中倒数第k个位置上的结点(k为正整数).若查找成功,算法输出该结点的data域的值,并返回1;否则,只返回0.要求:

(1)思想:
快慢指针,fast先走k步,然后fast指针和slow指针再一起走,直到fast指向NULL

(2)详细实现步骤:
快慢指针,fast先走k步,然后fast指针和slow指针再一起走,直到fast指向NULL

(3)实现:

int Back_k(ListNode* list,int k)
{
  //快慢指针,fast先走k步,fast和slow再一起走
  ListNode* fast = list;
  ListNode* slow = list;
  //fast先走k步
  while(k--)
  {
    //如果fast,走着走着为空了,说明k大于链表的长度了
    if(fast == NULL)
    {
		return 0;
    }
    fast = fast->next;
  }
  //fast和slow一起走,fast==NULL结束条件
  while(fast)
  {
    fast = fast->next;
    slow = slow->next;
  }
  printf("%d",slow->data);
  return 1;
}

23.假定采用带头节点的单链表保存单词,当俩个单词有相同的后缀时,可共享相同的后缀存储空间,例如,"loading"和"being"的存储映像如下图所示.
设str1和str2分别指向俩个单词所在单链表的头结点,请设计一个时间上尽可能高效的算法,找出由str1和str2所指向俩个链表共同后缀的起始位置。

(1)基本思想:
先计算出,str1和str2的结点个数,然后计算出他们节点个数之间的差值,让长的结点走到和短的结点一样长,然后一起走,直到他们的结点地址相同

(2)实现:

力扣https://leetcode.cn/problems/intersection-of-two-linked-lists/

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    int count1 = 0;
    int count2 = 0;
    struct ListNode* cur1 = headA;
    struct ListNode* cur2 = headB;
    while(cur1)
    {
       count1++;
       cur1 = cur1->next; 
    }
    while(cur2)
    {
       count2++;
       cur2 = cur2->next; 
    }
    //假如headA是短的,headB是长的
    struct ListNode* LongNode = headB;
    struct ListNode* ShortNode = headA;
    if(count1 > count2)
    {
        LongNode = headA;
        ShortNode = headB;
    }
    int count  = abs(count1-count2);
    while(count--)
    {
        LongNode = LongNode->next;
    }
    //俩个节点一起走
    while(LongNode && ShortNode)
    {
        if(LongNode == ShortNode)
        {
            return LongNode;
        }
        LongNode = LongNode->next;
        ShortNode = ShortNode->next;
        
    }
    return NULL;
}

(3)时间复杂度 o(n)

24.设计一个时间复杂度尽可能高效的算法,对于链表中data的绝对值相等的结点,仅保留第一次出现的结点而删除其余绝对值相等的结点。

(1)思想 :

以空间换时间,因为|data|<=n,所以开辟n+1个空间的数组[0,n],先将数组中的数全部重置为0,如果,链表中的每个元素值都会对应到数组中的下标,如果数组中没有放过这个值,第一次出现,令数组下标对应的值变为1,如果这个值再次出现,并且数组下标里面对应的值已经为1,那么就将其删除

(2)数据类型定义

#define n 100

struct ListNode
{
        int data;
        struct ListNode* next;      
}

(3)实现:

#define n 100
void Delete(ListNode* list1)
{
	//创建一个数组
	int* arr = (int*)malloc(sizeof(int) * (n+1));
	//重置为0
	memset(arr, 0, sizeof(int) * (n + 1));
	//扫描链表
	ListNode* cur = list1->next;
	ListNode* prev = list1;

	while (cur)
	{
		int num = abs(cur->data);
		if (arr[num] == 0)//第一次出现,保留
		{
			arr[num] = 1;//第一出现对应下标里面的数赋值为1
			prev = cur;
			cur = cur->next;
		}
		else//多次出现,删除
		{
			prev->next = cur->next;
			free(cur);
			cur = prev->next;
		}
	}
	free(arr);
}

b2554c3e54294aabbc1d99396b616714.png

 (4) 时间复杂度: o(m),因为题目给了单链表保存了m个整数  空间复杂度: o(n)

 25.

b222847e79fb40e08d7b3365219c402a.png

力扣https://leetcode.cn/problems/reorder-list/ (1)思想:

ab22efff140244358ca40e4726a7d832.png

先找中间结点,然后让中间结点的前一个结点的下一个节点置为空,让其形成俩个链表,然后将后半段链表逆置,然后将其插入到前半段节点中去,需要注意的是偶数个时,他们是一起结束正好对应插入,但如果是奇数个的时候,会多一个节点插入不进去,因为后半段结点的最后一个节点的前面正好与前半段匹配,已经置为空,所以将后半段奇数个,先将前面的偶数个插入,再找到排好的链表的尾,将后半段的最后一个节点插入

(2)实现:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


void reorderList(struct ListNode* head)
{
    if(head->next == NULL)
    {
        return;
    }
    //找中间结点
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    struct ListNode* slowprev = head;

    while(fast && fast->next)
    {
        fast = fast->next->next;
        slowprev = slow;
        slow = slow->next;
    }
    slowprev->next = NULL;
    //此时slow为中间结点
    //逆置后半段
    // printf("%d->",slowprev->val);

    //printf("%d->",slow->val);
    struct ListNode* prev = NULL;
    struct ListNode* cur = slow;
    struct ListNode* Next = NULL;
    while(cur)
    {
        Next = cur->next;
        cur->next = prev;
        prev = cur;
        cur = Next;
    }
    //printf("%d",prev->val);
    //将后半段插入前半段
    struct ListNode* ptail = prev;
    struct ListNode* phead = head;
    struct ListNode* pnext = ptail;
    //偶数个俩个都为空结束,但是奇数个,后半段会比前段多一个,所以先将他们按偶数个来进行插入,然后最后剩一个再自行插入
    while(ptail && phead)//如果只有一个节点[1],不判断ptail,那么pnext会报错,如果链表就为空,那么phead->next就为空,所以也必须判断phead是否为空
    {
        pnext = ptail->next;
        ptail->next = phead->next;
        phead->next = ptail;
        phead = ptail->next;
        ptail = pnext; 
    }
    struct ListNode* res = head;//寻找最后一个结点,让奇数个的还有一个结点插入
    struct ListNode* resprev = head;
    while(res)
    {
        resprev = res;
        res = res->next;
    }
    if(ptail)
    {
        ptail->next = resprev->next;
        resprev->next = ptail;
    }
}

(3)寻找中间结点的时间复杂度为o(n) 逆置的时间复杂度为o(n) 后半段结点插入到前半段的时间复杂度o(n)寻找最后一个结点的时间复杂度为o(n),所以整个算法的时间复杂度是o(n)

栈和队列的应用 综合应用题

02.按下图所示铁道车厢调度.................以使所有的软座车厢都被调整到硬座车厢之前

思路:
可以转化为一串字符串,是硬座H的入栈,软座S的直接输出,最后软座的都输出完了,再将栈中的硬座都输出即可

void  Adjust(char *train)
{	
	char* p = train;
  	Stack s1;
  	StackInit(s1);
  	while(*p)
    {
      if(*p == 'H')
      {
        //硬座入栈
        StackPush(s1,*p);
      }
      else
      {
		//如果是软座,直接输出
        printf("%s",*p);
      }
      p++;
    }
  //硬座都入栈了,软座都输出了,现在把硬座都输出
  while(!StackEmpty(&s1))
  {
    printf("%s",stackTop(&s1));
    StackPop(&s1);
  }
}

本文仅为自己的拙见,并参考了王道数据结构书,可以为有需要的人参考!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值