单链表OJ

本文深入探讨了链表数据结构的高级操作,包括基于给定值的链表分割、删除重复节点以及判断链表是否为回文结构。通过具体算法实现,提供了理解和实践链表操作的实用指南。

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

  • 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前
ListNode* partition(ListNode* pHead, int x)
{
 ListNode *lesshead = (ListNode *)malloc(sixeof(ListNode));
 ListNode *morehead = (ListNode *)malloc(sizeof(ListNode));
 //设置两个哑结点,作为分割后的链表的头结点,但是它没有有效值。
 ListNode *pless = lesshead;
 ListNode *pmore = morehead;
 ListNode *cur = pHead;
  while (cur)
  {
    if (cur->val < x)
    {
      pless->next = cur;
      pless = pless->next;
    }
    else
    {
      pmore->next = cur;
      pmore = pmore->next;
    }
    cur = cur->next;
  }
 pless->next = moreless->next;//第一个链表的尾部连接到第二个链表的头。
 pmore->next = NULL;
 pHead = lesshead->next;//要最后设置新的头结点,防治发生堆溢出。
 free (lesshead);
 free (morehead);//把前面设置的具有辅助功能的哑结点释放掉。
 return pHead;
 }
  • 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。
    例如,链表1->2->3->3->4->4->5 处理后为 1->2->5。
ListNode* deleteDuplication(ListNode* pHead)
{
 ListNode *prev = NULL;
 ListNode *cur = pHead;
 ListNode *tail = pHead->next;
   if (pHead==NULL || pHead->next==NULL)
   {return pHead;}
   //当没有结点或许只有一个结点的时候,不会出现有重复的数字,直接返回。
   while (tail)//保证有下一个可以和cur比较的数字
   {
     if(cur->val == tail->val)
     {
      tail =tail->next;
      while (tail)//查找重复的结点
      {
       if (cur->val != tail->next)
       break;
       tail = tail->next;
      }
      while (cur != tail)//删除重复的结点,不能直接删除cur,cur的位置还需要标记。
      {
       ListNode *ptmp = cur;
       cur = cur->next;
       free (ptmp);
      }
      if (prev == NULL)
      {
       pHead = tail;
      }
      else
      {
       prev->next = tail;
      }
      if (tail)
      {
        tail = tail->next;//开始下一轮重复值得查找。
      }
    }
  else
  {
   prev = cur;
   cur = tail;
   tail = tail->next;
  }
 }
  return pHead;
}
  • 对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。

     测试样例:
     1->2->2->1
     返回:true
    
ListNode *ListReverse (ListNode *head)
{
 ListNode *prev = NULL;
 ListNode *cur = head;
 ListNode *tail = NULL;
  while (cur)
  {
   tail = cur->next;
   cur->next = prev;
   prev = cur;
   cur = tail;
  }
  return prev;//一定要返回新链表的头结点。
}

bool chkPalindrome(ListNode* head)
{
  ListNode *fast = head;
  ListNode *slow = head;
  while (fast && fast->next)//寻找中间结点的位置。
  {
   fast = fast->next->next;
   if (fast)
   slow = slow->next;
  }
  ListNode *Listlater = slow->next;
  slow->next = NULL;
  ListNode *Listbefer = head;
  Listlater = ListReverse(Listlater);
  while (Listbefer && Listlater)
  {
   if (Listbefer != Listlater)
   {
    return false;
   }
   else
   {
    Listbefer = Listbefer->next;
    Listlater = Listlater->next;
   }
  }
 return true;
}
### 如何在单链表中对信息进行分类 单链表作为一种常见的数据结构,其基本特性是非连续、非顺序的存储方式,其中每个节点由两部分组成:元素(即实际存储的数据)和指向下一个节点的指针[^1]。为了实现在单链表中对信息进行分类的操作,可以采用以下几种常见的方式: #### 方法一:基于特定条件重新构建多个子链表 可以通过遍历整个单链表并根据某些预定义的条件将节点分配到不同的子链表中。例如,假设需要按照数大小分为两类,则可以在一次遍历过程中创建两个新的链表分别保存满足条件和不满足条件的节点。 ```java public class Node { int data; Node next; public Node(int d) { this.data = d; } } // 假设按data是否大于某个阈threshold来进行分类 public void classifyByValue(Node head, int threshold) { Node lessHead = null, lessTail = null; // 小于等于threshold的部分 Node greaterHead = null, greaterTail = null; // 大于threshold的部分 while (head != null) { if (head.data <= threshold) { if (lessHead == null) { lessHead = lessTail = new Node(head.data); } else { lessTail.next = new Node(head.data); lessTail = lessTail.next; } } else { if (greaterHead == null) { greaterHead = greaterTail = new Node(head.data); } else { greaterTail.next = new Node(head.data); greaterTail = greaterTail.next; } } head = head.next; } // 可选操作:打印或进一步处理这些链表... } ``` 此方法的时间复杂度主要取决于原始列表长度 \(n\) 和具体实现细节,通常为 \(O(n)\)[^2]。 #### 方法二:原地修改法——调整现有链表内部链接关系完成分类 这种方法不需要额外空间去建立新链表而是直接改变当前链表里各节点之间的相对位置达到目的。比如对于整数类型的节点来说可能希望正负分开排列;或者字符串类型则依据字母顺序重组等等。 下面给出一个简单的例子展示如何利用双指针技术来交换相邻两项直到所有符合条件项都移到前面为止(类似于冒泡排序的思想): ```java public boolean shouldMoveToFront(Node node){ return true;// Define your own logic here. } public void moveElementsToBeginning(Node head){ if(null==head || null == head.next)return ; Node prev=null,current=head,nextNode=null; do{ current=current.next; if(current!=null &&shouldMoveToFront(current)){ if(prev!=null){prev.next=current;} nextNode=current.next; current.next=head; head=current; current=nextNode; }else{ prev=current; } }while(current!=null); System.out.println("After moving elements to beginning:"); printList(head); } private static void printList(Node n){ while(n!=null){ System.out.print(n.data+" "); n=n.next; } System.out.println(); } ``` 上述代码片段展示了如何通过不断向前移动满足 `shouldMoveToFront` 函数判定标准的节点至头部从而形成一种形式上的“分类”。当然实际情况下的判断准则会更加多样化也更复杂一些[^3]. #### 注意事项 当涉及到删除某类特殊项目时务必小心边界状况以及断开后的重连过程以免丢失重要数据或是造成内存泄漏等问题发生。另外,在执行任何大规模变动前最好先备份初始状态以便必要时候回滚恢复[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值