数据结构 顺序表/ 链表oj 曲四

Posted 一个正直的男孩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构 顺序表/ 链表oj 曲四相关的知识,希望对你有一定的参考价值。

leetcode (27)移除元素

  • 基于顺序表

🙉:题目

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

🙊:案例


🐒:思路

快慢针:

  1. 创建dest和sur指针
  2. sur去找val值如果不是就吧sur赋值给dest
  3. sur等于val则一直迭代

图解:


🐵:代码

int removeElement(int* nums, int numsSize, int val)
{
    int strc =0;
    int dest=0;
    while(strc<numsSize)
    {
       if(nums[strc]==val)
       {
           strc++;
       }
       else
       {
            nums[dest]=nums[strc];
            dest++;
            strc++;
       }
    }
   
   return dest;
}

leetcode (26) 删除数组重复项

🙉:题目

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成

🙊:案例

🐒:思路
快慢指针

  1. 创建dest和sur指针
  2. 不同sur与dest是否相同,不同赋值dest+1(不是dest+1那么头元素就被覆盖了)
  3. 相同sur迭代

图解


🐵:代码

int removeDuplicates(int* nums, int numsSize)
{
     int strc =0;
     int dest=0;
     while(strc<numsSize)
     {
         if(nums[strc]==nums[dest])
         {
             strc++;
         }
         else
         {
             dest++;
             nums[dest]=nums[strc];
             strc++;
         }
     }
     if(numsSize!=0)
     {
       return dest+1;
     }
     return 0;

}

注意他可能传的是空数组,如果为空那么返回dest+1打印就越界了

leetcode (88) 合并俩个有序数组

🙉:题目

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。你可以假设 nums1 的空间大小等于 m + n,这样它就有足够的空间保存来自 nums2 的元素。

🙊:案例

🐒:思路
思路一(暴力解法):

  1. 直接把数组二插入数组一后面
  2. qsort排序

图解


🐵:代码

int compar(const void *p1,const void*p2)
{
     return (*(int*)p1)-(*(int*)p2);
}
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
    int a=m;
  for(int i =0;i<n;i++)
  {
      nums1[a]=nums2[i];
      a++;
  }
  qsort(nums1,n+m,sizeof(int),compar);
}

思路二

  1. 都指向需要比最后的元素
  2. 比较俩数组大小,大的放到一数组最后一个位置
  3. 依次迭代迭代

图解


🐵:代码

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
    int i1=m-1,i2=n-1;
    int tail=m+n-1;
   

    while(i1>=0&&i2>=0)
    {
      if(nums1[i1]>nums2[i2])
      nums1[tail--]=nums1[i1--];
      else
       nums1[tail--]=nums2[i2--];
    }
    while(i2>=0)
    {
        nums1[tail--]=nums2[i2--];
    }
}

leetcode (203) 移除链表元素

🙉:题目

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

🙊:案例

🐒:思路
思路一

  1. 把指针给cur
  2. cur找cur下一个节点的值是否和val一样
  3. 一样释放,不一样迭代
  4. 注意(头的值也可能等于val),(且可能是空链表)

图解


🐵:代码

struct ListNode* removeElements(struct ListNode* head, int val)//返回指针接口
{
    if(head==NULL)
    {
        return NULL;
    }
   struct ListNode*  cur=head;
   struct ListNode*  hind=head;
  while(cur->next)
  {
      if(cur->next->val==val)
      {
        struct ListNode*next=cur->next->next;
        free(cur->next);
        cur->next=next;
      }
      else
      {
          cur=cur->next;
      }     
  }
    if(head->val==val)
      {
        struct ListNode*next=head->next;
        free(head);
        head=next;
      }
    return head;
}

思路二

  1. 三个指针,cur,list,tail
  2. cur找相等val的值
  3. list为这个新链表的头
  4. tail为cur的上一个节点
  5. 找到free,迭代
  6. 没找到吧cur给tail,迭代

如图所示


🐵:代码

struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode*cur=head;
    struct ListNode*list=NULL;  struct ListNode*tail=NULL;
    while(cur)
    {
        struct ListNode*next=cur->next;
      if(cur->val==val)
      {
         free(cur);
      }
      else
      {
          if(list==NULL)
          {
              list=cur;
              tail=list;
          }
          else
          {
            tail->next=cur;
          }
      }
      cur=next;
    }
   return list;
}

leetcode (206) 反转链表

🙉:题目

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表

🙊:案例

🐒:思路
思路一
创建节点法:

  1. 在老节点后面创建新节点(应为头插会改变原来链表的顺序死循环
  2. 剥离开辟的节点插入新链表
  3. 链接老链表与节点
  4. 迭代(这种方法不太推荐容易搞混且效率不是很高)


🐵:代码

struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* newlist=NULL;
    struct ListNode*cur=head;
    
    while(cur)//在老节点后插入一个新节点
    {
        struct ListNode*next=cur->next;
        struct ListNode*newnode=(struct ListNode*)malloc(sizeof(struct ListNode));
        newnode->next=next;
        newnode->val=cur->val;
        cur->next=newnode;
        cur=cur->next->next;
    }
    cur=head;
    
    while(cur)//剥离新节点并插入到新链表上
    { 
       struct ListNode* curnext=cur->next;
        cur=curnext->next;
        curnext->next=NULL;
        if(newlist==NULL)
        {
            newlist=curnext;
        }
        else
        {
            struct ListNode*oldhead =newlist;
            newlist=curnext;
            newlist->next=oldhead;
        }
        
    }
    return newlist;
}

思路二
翻转链表法:

  1. 反转链表链接顺序
  2. 头变尾,尾变头
  3. 三个指针完成置换

如图所示:


struct ListNode* reverseList(struct ListNode* head)
 {
     if(head==NULL)
     {
        return NULL;
     }
     struct ListNode*n1=NULL;
     struct ListNode*n2=head;//头
     struct ListNode*n3=head->next;//头后面一个节点
     while(n2)
     {
        n2->next=n1;
        n1=n2;
        n2=n3;
        if(n3!=NULL)
        n3=n3->next;
     }
      return n1;
 }

牛客 链表倒数第k个节点

🙉:题目

输入一个链表,输出该链表中倒数第k个结点。
其实就是从后往前数第k个节点

🙊:案例

🐒:思路
快慢指针

  1. 快指针先走k步(题比较阴间:给你一个空链表
  2. 然后慢指针在和块指针一起走
  3. 代码很简单,但是想法很难
  4. (这个方法一般人可想不到,幸好我不是一般人,我是二般人,好吧我也是一般人,大佬思路。多提一嘴,孰能生巧(勤加练习+复盘)+多见风浪(多刷题),以后碰到hr都不慌)

图解:


🐵:代码

 ListNode* FindKthToTail(ListNode* pListHead, unsigned int k)
    {
          ListNode* fast=pListHead;
          ListNode* slow=pListHead;
        while(k--)
        {
            if(fast==NULL)
            {
                return NULL;
            }
            fast=fast->next;
        }
        while(fast)
        { 
            fast=fast->next;
            slow=slow->next;
        }
        return slow;
    }

leetcode (879) 链表的中间节点

🙉:题目

给定一个头结点为 head 的非空单链表,返回链表的中间结点,如果有两个中间结点,则返回第二个中间结点

🙊:案例

🐒:思路
快慢指针

  1. fast走俩步
  2. slow走一步
  3. 这样就可以取到中间节点
  4. 注意链表可能会有基数个or偶数个节点,所以要迭代的话条件要满足

图解:


🐵:代码

struct ListNode* middleNode(struct ListNode* head)
{
     struct ListNode*fast=head;
     struct ListNode*slow=head;
     while(fast&&fast->next)
     {
         fast=fast->next->next;
         slow=slow->next;
     }

return slow;
}

leetcode (21)合并连个有序链表

🙉:题目

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

🙊:案例

🐒:思路
思路一:
一一对比法

  1. 俩链表从前往后一一对比
  2. 小的尾插到新链表


🐵:代码

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2)
{
    if(l1==NULL&&l2==NULL)
    {
        return NULL;
    }
    struct ListNode*newlist=(struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode*cur=newlist;
    while(l1&&l2)
    {
        if(l1->val<l2->val)
        {
            cur->next=l1;
            l1=l1->next;

        }
        else
        {
            cur->next=l2;
            l2=l2->next;
        }

        cur=cur->next;
    }
    if(l1==NULL)
     cur->next=l2;
    if(l2==NULL)
     cur->next=l1;

    struct ListNode*head=newlist->next;
    free(newlist);
    return head;
}

这里用到了带头单链表,俗称哨兵位,(其实也可以不用)


思路二:
直插法
3. 判断俩个连表的头节点小的付给新链表
4. 一一对比
5. 上面方法的另一种
🐵:代码

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2)
{
    if(l1==NULL)
        return l2;
    if(l2==NULL)
        return l1;

    struct ListNode*n1=l1;
    struct ListNode*n2=l2;
    struct ListNode*newhead=NULL;
    struct ListNode*tail=NULL;
    if(n1->val < n2->val)
    {
       newhead=tail=n1;
       n1=n1->next;
    }
        else
    {
        newhead=tail=n2;
        n2=n2->next;
            }

    while(n1&&n2)
    {
        if(n1->val<n2->val)
        {
            tail->next=n1;
            tail=n1;
            n1=n1->next;
      }
        else
        {
            tail->next=n2;
           tail=n2;
            n2=n2->next;
        }
    }
    if(n1)
        tail->next=n1;
   if(n2)
        tail->next=n2;
  return newhead;

思路三:
直插法2

  1. 判断俩个连表的头节点小的付给新链表
  2. 一一直接插入到练表中更改他们的链接方式(这个就留给你去写吧)


牛客 (综合题) 链表的回文结构

🙉:题目

对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。

🙊:案例

其实就是这个链表是不是中心对称

🐒:思路
快慢指针,翻转链表,比大小

  1. 找到中间值(快慢指针)
  2. 从中间值开始反转成为一个新的链表(翻转链表)
  3. 一一比大小
  4. 👆有动图详解
    🐵:代码
class PalindromeList {
public:
   ListNode* listmiddle(ListNode* A)
   {
       ListNode*fast=A;
       ListNode*slow=A;
       while(fast&&fast->next)
       {
           slow=slow->next;
           fast=fast->next;
       }
       return slow;
       
   }

    ListNode* reverse(ListNode* middle)
    {
       ListNode* cur=middle;
       ListNode*tail=NULL;
       while(cur)
       {
           ListNode*next=cur->next;
           cur->next=tail;
           tail=cur;
           cur=next;  
       }
        return middle;
    }  
    bool chkPalindrome(ListNode* A) 
    {
        ListNode*middle=listmiddle(A);
        ListNode*B=reverse(middle);
        while(A&&B)
        {
            if(A->val!=B->val)
            {
                return false;
            }
            A=A->next;
            B=B->next;
        }
          return true;  
    }
};

leetcode (160) 相交链表

🙉:题目

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null
图示两个链表在节点 c1 开始相交:

🙊:案例

🐒:思路
快慢指针

  1. 先比较俩个链表的长度并求出差值
  2. 快的先走差值步
  3. 然后在一起走,一一比较(比较的是地址

图解:


🐵:代码

int size(struct ListNode *head)
{
    int count=0;
    while(head)
    {
   ++count;
   head=head->next;
    }
    return count;
}
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
     int countA=size(headA);
     int countB=size(headB);
     
     //你不知道那个大其实也可以比较count,这里就是少写了一个else
    struct ListNode*fast=headA;
    struct ListNode*slow=headB;
     if(countA<countB)
     {
        fast=headB;
        slow=headA;
     }

    int foot=abs(countA-countB);
    while(foot--)
    {
         fast=fast->next;
    }
    while(fast&&slow)
    {
        if(fast==slow)
        {
            return fast;
        }
        fast=fast->next;
        slow=以上是关于数据结构 顺序表/ 链表oj 曲四的主要内容,如果未能解决你的问题,请参考以下文章

看了有助于你面试的单链表的OJ面试题

03线性表之链表

数据结构---线性表(链表OJ)

每天学习亿点点系列——OJ203题:移除链表元素

数据结构---线性表(链表OJ2)

数据结构OJ题——移除链表元素