剑指offer之链表

//剑指offer 之 链表

//面试题6 从尾到头打印链表
/*****************************************************************************************
问题描述:
输入一个链表的头节点,从尾到头反过来打印出每个节点的值
链表节点定义如下:
struct ListNode{
   int m_nValue;
   ListNode* m_pNext;
};
******************************************************************************************/

//解答如下:
//首先实现栈 由于是用C语言 所以需要自己构建栈
int a[100];  //用数组实现栈
int p = 0;       //栈顶
//入栈
void push(int a[], int value)
{
  if(p == 99)
  {
    /*扩大栈*/
  }
  a[p++] = value;
}

//出栈
int pop(int a[]);
{
  if(p <= 25)
  {
     /*压缩栈*/
  }

  int res = a[--p];
  return res;
}

//判断是否空栈
int isEmpty(int a[])
{
  return p == 0;
}

//从尾到头打印链表
//栈实现
void PrintListReversingly_Iteratively(ListNode* pHead)
{
  if(pHead == NULL) return;
  ListNode *tmp = pHead;

  while(tmp != NULL)
  {
    push(a,tmp->value);
    tmp = tmp->m_pNext;
  }

  int value;
  while(!isEmpty(a))
  {
     value = pop(a);
     printf("%d-",value);
  }
}

//递归实现
void PrintListReversingly_Recursively(ListNode* pHead)
{
   if(pHead == NULL) return;
   if(pHead->m_pNext != NULL)
   {
    PrintListReversingly_Recursively(pHead->m_pNext);
   }
   printf("%d-",pHead->m_nValue);
   return;
}

//面试题18:删除链表的节点
/**********************************************************
18-1:
在O(1)时间内删除链表节点
给定单向链表的头指针和一个节点指针
链表节点定义如问题6所述, 函数定义如下
***********************************************************/

void DeleteNode(ListNode** pListNode, ListNode* pToBeDeleted)
{
  /*
  1)节点位于中间:将下一个节点内容复制到该节点,删除下一个节点 时间复杂度为O(1)
  2)节点位于尾部:从头遍历至该节点;
  3)链表只有一个节点:删除并将头指针置为NULL
  */
  if( pListNode == NULL || pToBeDeleted == NULL) return;
  //处理情况1
  if(pToBeDeleted->m_pNext != NULL)
  {
    ListNode *pNext = pToBeDeleted->m_pNext;
    pToBeDeleted->m_nValue = pNext->m_nValue;
    pToBeDeleted->m_pNext = pNext->m_pNext;
    free(pNext);
    pNext = NULL;
  }
  //处理情况3
  else if(*pListNode == pToBeDeleted)
  {
    free(pToBeDeleted);
    *pListNode = NULL;
    pToBeDeleted = NULL;
  }
  //处理情况2
  else
  {
    ListNode *pNext = *pListNode;
    while(pNext->m_pNext != pToBeDeleted)
    {
      pNext = pNext->m_pNext;
    }
    pNext->m_pNext = NULL;
    free(pToBeDeleted);
    pToBeDeleted = NULL;
  }
  /*
  复杂度分析:假设链表上有n个节点
  对于前n-1个节点,时间复杂度为O(1)
  对于最后一个节点,时间复杂度为O(n)
  因此,总的为((n-1)*O(1) + O(n)) /n = O(1)
  */
}

/*********************************************
18-2:
在一个排序链表中,如何删除重复节点?
**********************************************/
void DeleteDuplication(ListNode** pHead)
{
  if(pHead == NULL || *pHead == NULL) return;

  ListNode* pPreNode = NULL; //围绕前面节点 此节点 此节点的后续节点 操作
  ListNode* pNode = *pHead;

  while(pNode != NULL)
  {
    ListNode *pNext = pNode->m_pNext;
    int deleteFlag = 0;
    //判断是否需要删除操作
    if(pNode->m_nValue == pNext->m_nValue) deleteFlag = 1;
    //不需要删除,则指针向后移
    if(!deleteFlag)
    {
      pPreNode = pNode;
      pNode = pNode->m_pNext;
    }
    else
    {
      int value = pNode->m_nValue;
      ListNode *pToBeDel = pNode;
      //不断执行删除操作 从删除当前节点开始
      //前面节点用于连接后面的节点
      while(pToBeDel != NULL && pToBeDel->m_nValue == value)
      {
        pNext = pToBeDel->m_pNext;
        free(pToBeDel);
        pToBeDel = pNext;
      }
      //将需要删除的节点已删除完毕    判断之前删除的节点中是否包含头节点
      if(pPreNode == NULL) *pHead = pNext;
      else
           pPreNode->m_pNext = pNext;

      pNode = pNext;
    }
  }
}

/***********************************************************************************************
面试题22:链表中倒数第k个节点
输入一个链表,输出该连表中倒k个节点

双指针 注意判断边界以及k小于链表个数
************************************************************************************************/
ListNode* FindKthToTail(ListNode* pListNode, unsigned int k)
{
  if(pListNode == NULL || k <= 0) return NULL;

  ListNode *pAhead = pListNode;
  ListNode *pBhead = NULL;

  for(unsigned int i=0;i<k-1;i++)
  {
     if(pAhead->m_pNext != NULL)
     {
      pAhead = pAhead->m_pNext;
     }
     else
     {
      return NULL;
     }
  }

 pBhead = pListNode;
 while(pAhead != NULL)
 {
   pBhead = pBhead->m_pNext;
   pAhead = pAhead->m_pNext;
 }

 return pBhead;
}

/*
拓展:求链表中间节点
方法:快慢指针
*/

/***********************************************************************************************
面试题23: 链表中环的入口节点
如果一个链表中有环,如何找出出环的入口节点
解题思路:
1)先判断有无环存在:
  设定快慢指针,若慢指针会追上快指针,则存在环
2)得出环中节点数目n:
  在1)基础上,返回两指针相遇点,该点在环内,然后让指针从
  该点出发走一圈,记录数目
3)根据环中节点数目n:
  还是两指针,一个在另一个前面n步,同时走,则相遇处为入口
************************************************************************************************/
ListNode* MeettingNode(ListNode *pHead)
{
  if(pHead == NULL || pHead->m_pNext == NULL) return NULL;

  ListNode *pFast = pHead;
  ListNode *pSlow = pHead;

  while(pFast != NULL && pSlow!= NULL)
  {
     if(pFast == pSlow) return pFast;

     pSlow = pSlow->m_pNext;
     pFast = pFast->m_pNext;
     if(pFast != NULL)//当链表中无环时起到了判断作用
       pFast = pFast->m_pNext;
  }
  return NULL;
}

ListNode* EntryNodeOfLoop(ListNode* pHead)
{
    ListNode *meetingNode = MeettingNode(pHead);
    if(meetingNode == NULL) return NULL;

    //得环中节点数目n
    int num_of_nodes = 1;
    ListNode *tmp1 = meetingNode;
    while(tmp1->m_pNext != meetingNode)
    {
      num_of_nodes++;
      tmp1 = tmp1->m_pNext;
    }

    //移动第一个指针 n步
    tmp1 = pHead;
    for(int i=0;i<num_of_nodes;i++)
    {
      tmp1 = tmp1->m_pNext;
    }

    //两指针一起走
    ListNode *tmp2 = pHead;
    while(tmp1 != tmp2)
    {
      tmp1 = tmp1->m_pNext;
      tmp2 = tmp2->m_pNext;
    }

    return tmp1;
}

/*****************************************************************************************
面试题24: 反转链表
定义一个函数,输入一个链表的头节点,
反转该链表并输出反转后链表的头节点
*******************************************************************************************/
ListNode *ReverseList(ListNode* pHead)
{
  if(pHead == NULL || pHead->m_pNext == NULL) return pHead;

  ListNode *pPreNode = NULL;
  ListNode *pNode = pHead;
  ListNode *pNext = NULL;
  ListNode *pReverseHead = NULL;
  while(pNode != NULL)
  {
      pNext = pNode->m_pNext;
      //判断是否到尾部
      if(pNext == NULL) pReverseHead = pNode;

      pNode->m_pNext = pPreNode;
      pPreNode = pNode;
      pNode = pNext;
  }

  return pReverseHead;
}

/*********************************************************************************************
面试题25:合并两个排序链表
输入两个递增排序的链表,合并这两个链表并使新链表中的
节点仍然是排序的
*********************************************************************************************/

ListNode *Merge(ListNode *pHead1, ListNode *pHead2)
{
   if(pHead1 == NULL) return pHead2;
   else if(pHead2 == NULL) return pHead1;

   ListNode* pMergedHead = NULL;

   if(pHead1->m_nValue < pHead2->m_nValue)
   {
       pMergedHead = pHead1;
       pMergedHead -> m_pNext = Merge(pHead1->m_pNext,pHead2);
   }
   else
   {
       pMergedHead = pHead2;
       pMergedHead -> m_pNext = Merge(pHead1,pHead2->m_pNext);
   }
   return pMergedHead;
}

/*
面试题35 复杂链表的复制
实现函数ComplexListNode * Clone(ComplexListNode* pHead);
复制一个复杂链表,每个节点除了有一个m_pNext指针指向下一个节点,还有一个
m_pSibling指针指向链表中的任意节点orNULL
*/
struct ComplexListNode{
  int m_nValue;
  ComplexListNode* m_pNext;
  ComplexListNode* m_pSibling;
};

原文地址:https://www.cnblogs.com/dzy521/p/9597707.html

时间: 2024-10-19 11:29:39

剑指offer之链表的相关文章

【剑指offer】链表倒数第k个节点

转载请注明出处:http://blog.csdn.net/ns_code/article/details/25662121 在Cracking the Code Interview上做过了一次,这次在九度OJ上测试,AC. 题目描述: 输入一个链表,输出该链表中倒数第k个结点.(hint: 请务必使用链表.) 输入: 输入可能包含多个测试样例,输入以EOF结束.对于每个测试案例,输入的第一行为两个整数n和k(0<=n<=1000, 0<=k<=1000):n代表将要输入的链表元素的

剑指offer (5) 链表插入删除

我们在操作链表的时候,必须注意以下事项: 1. 链表指针为NULL的情况 2. 插入删除涉及到 链表第一个节点时,需要修改 链表的第一个节点: a. 因为 c语言都是传值的,如果需要修改一个变量,就必须通过 指向该变量的指针(即该变量的地址) 例如:例如 修改 int a,则输入参数必须是 int* a, 修改a 则是: *a = b; (b为int) 我们需要修改一个指针时,就必须通过 该指针的地址,也就是 指向该指针的指针,即二级指针 例如  修改 int* a, 则输入参数必须是 int*

【Java】 剑指offer(22) 链表中倒数第k个结点

正文 本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 输入一个链表,输出该链表中倒数第k个结点.为了符合大多数人的习惯,本题从1开始计数,即链表的尾结点是倒数第1个结点.例如一个链表有6个结点,从头结点开始它们的值依次是1.2.3.4.5.6.这个链表的倒数第3个结点是值为4的结点. 思路 第一直觉是先从头开始遍历,计算链表个数n,然后重新遍历,第n-k+1个结点即为所需要的结点.但是需要遍历2次.后面采用了栈进行实现该

剑指offer 复杂链表的复制

题目描述: 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head.(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空) 思路:要进行深拷贝,需要每次都new一个节点出来,利用unordered_map, 2.3 138. Copy List with Random Pointer https://leetcode.com/problems/copy-list-with-random-poi

两个链表的第一个公共结点(剑指offer)+链表

两个链表的第一个公共结点 参与人数:1171时间限制:1秒空间限制:32768K 通过比例:31.25% 最佳记录:0 ms|0K(来自  running) 题目描述 输入两个链表,找出它们的第一个公共结点. 链接:http://www.nowcoder.com/practice/6ab1d9a29e88450685099d45c9e31e46?rp=2&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

剑指Offer 14. 链表中倒数第k个结点 (链表)

题目描述 输入一个链表,输出该链表中倒数第k个结点. 题目地址 https://www.nowcoder.com/practice/529d3ae5a407492994ad2a246518148a?tpId=13&tqId=11167&rp=3&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking 思路 三个特例:如果输入的链表为空:k大于链表的长度:k为0的情况.对于正常情况,设置两个指针分

剑指offer --合并链表

题目:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则. 解法://递归解法 public class MixLink { /* public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } }*/ public class Solution { public ListNode Merge(ListNode list1,ListNo

《剑指offer》链表中环的入口结点

[ 声明:版权所有,转载请标明出处,请勿用于商业用途.  联系信箱:[email protected]] 题目链接:http://www.nowcoder.com/practice/253d2c59ec3e4bc68da16833f79a38e4?rp=3&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking 题目描述 一个链表中包含环,请找出该链表的环的入口结点. 思路 与这道题是一样的,详细解析在此:ht

剑指OFFER之链表中倒数第k个节点(九度OJ1517)

题目描述: 输入一个链表,输出该链表中倒数第k个结点.(hint: 请务必使用链表.) 输入: 输入可能包含多个测试样例,输入以EOF结束.对于每个测试案例,输入的第一行为两个整数n和k(0<=n<=1000, 0<=k<=1000):n代表将要输入的链表元素的个数,k代表要查询倒数第几个的元素.输入的第二行包括n个数t(1<=t<=1000000):代表链表中的元素. 输出: 对应每个测试案例,若有结果,输出相应的查找结果.否则,输出NULL. 样例输入: 5 2 1