Leetcode reorder List

本文介绍了一种链表处理算法,即将链表L0→L1→…→Ln-1→Ln重新排列为L0→Ln→L1→Ln-1→L2→Ln-2→…的方法。通过寻找中间节点、翻转后半部分链表及合并操作实现原地修改,不改变节点值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Given a singly linked list LL0L1→…→Ln-1Ln,
reorder it to: L0LnL1Ln-1L2Ln-2→…

You must do this in-place without altering the nodes' values.

For example,

Given {1,2,3,4}, reorder it to {1,4,2,3}.

题目如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */


不同于一般的链表翻转,这里要求是链表要交叉着进行倒叙。  

我的思路是:先寻找链表的中间节点,然后在此处对后半段的链表进行翻转,这里就得到前后两段顺序正常的链表,然后进行合并。程序用到了3个最

基本的链表操作,寻找中间节点findMiddle(), 链表翻转reverseList(),以及最终的合并reorderList()。

(1)寻找中间节点findMiddle()

  给定一个链表*head, 利用一个first和second指针,前者每次前进一步,后者每次前进2步,当遍历完链表之后,first指针就是需要找的中间节点。但在leetcode上提交时几次出现了Runtime error 的问题。因为大多数寻找链表中间节点的操作只需要返回中间节点就够了,而在此处,除了找到中间节点first,还需要在此处将前后断开。在找到中间点first之后,要断开链表需要知道first的前驱,由于是单链表,无法直接得到前驱,所以我用了一个pre来保存。调用该函数后,first和second分别指向链表前一半和后一半的表头,代码如下:

struct Node* findMiddle(struct Node* head)
{
	  struct Node* first, *second ,*pre;
	  first = second = head;
	  if(NULL== head | NULL ==  head ->next)
		  return head;
	  else
	  {
		    while(second != NULL && second -> next != NULL )
			{
				  pre = first;
				  first =  first -> next;
				  second =  second -> next->next;
			}
			     second =  first;
				 pre -> next =  NULL;
				 first =  head;
				return second;  
	  }
}

(2)翻转链表操作 reverse().给定一个链表*head,对其翻转操作。这里我用的是原址翻转,也就是不增加额外空间,在原地对链表进行操作,需要的是改变原链表的指针指向,用到3个变量pre, cur, nex,代码如下:

//reverse the totall Linklist 'head' without extra space
struct Node* reverse(struct Node* head)
{
	     struct Node* pre = NULL;
		struct Node* cur= head;
		struct Node* nex=  NULL;
		while(cur != NULL)
		{
			  nex =  cur -> next;
			  cur -> next  = pre;
			  pre = cur;
			  cur = nex;
		}
		head -> next = NULL;
		head = pre;
		return pre;
}

(3)在归并之前,首先调用寻找终点函数, mid = findMiddle(*head), 得到前后两段链表,然后对后半段链表调用上个函数mid = reverse(mid)得到后半段已经逆序后的mid链表。至此,已经有了两段链表head和mid,现在需要对他们进行挨个插入排序,即在head的第1,2,3,4,...个节点之后分别插入mid的第1,2,3,...个节点。

    这里有个细节需要注意,就是对于两段链表节点个数不一样的问题的边界处理。由于mid包含了原链表的中点,所以如果总节点个数为奇数个,那么必然是mid链表比head链表多出1个。每次进行处理的时候,需要先用head_nex和mid_nex来保存当前两段链表的后驱节点,然后插入过程需要进行判断,如果head_nex为空,则插入mid_nex,否则插入head_nex(这段逻辑大家好好体会下),代码如下:

void reorderList(ListNode *head)
  {
        if(head==NULL) 
            return;
        else
        {
         struct ListNode* mid =  findMiddle(head);
	     mid = reverse(mid);	                 // return the List reversed from the middle
	     struct ListNode* head_nex, *mid_nex;
	     head_nex = mid_nex = NULL;
	     struct ListNode* newlist = head;
	     if(head ==  mid) 
	        ;
	     else
         while(head !=NULL  && mid != NULL)
	       {
		      head_nex =  head -> next;
		      mid_nex =  mid-> next;                 //insert the second element into the first Linklist
			  mid -> next = (head -> next == NULL)?mid->next:head->next;    
			  head -> next =  mid;   
			  head =  head_nex;
			   mid =  mid_nex;
	      }  
        }  
      // 
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值