链表经典问题

反转链表

反转链表有两种方法,分别是迭代法和递归法。

迭代法

有两种思路,一种是把头结点的下一个节点一次移动到第一个,直到头结点变成最后一个;第二种是依次把第一个节点移动到尾结点的后面,直到尾结点变成第一个。

递归法

假设除头节点以外的所有节点都已反转,把头结点以后的链表看做一个整体,则需要做的操作就是把头结点移动到它的后面。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head==NULL||head->next==NULL)
            return head;
        ListNode *newhead=reverseList(head->next);
        head->next->next=head;
        head->next=NULL;
        return newhead;

    }
};

移除指定元素

迭代法

用差距为一的前后两个指针同时行进,前面一个指针的节点数据是如果等于指定数据则删除。

递归法

首先假设除头结点外的链表中的指定元素都删除了,然后只要保证头结点的数据不等于指定,如果等于则删除头结点。

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        if(head==NULL)
            return NULL;
        head->next=removeElements(head->next,val);
        if(head->val==val)
            return head->next;
        else
            return head;
    }
};

奇偶链表

问题描述:给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。

这个问题与反转链表有点类似,先找到链表的尾结点,其次把头结点的下一个节点移到尾结点的后面,最后头尾节点各后移一位,循环次数为链表长度n/2。

class Solution {
public:
    ListNode* oddEvenList(ListNode* head) {
        if(head==NULL||head->next==NULL||head->next->next==NULL)
        return head;
        ListNode *tail,*a,*b;
        tail=head;a=head;
        int i=1;
        while(tail->next!=NULL)
        {
            tail=tail->next;
            i++;
        }
        for(int j=0;j<i/2;j++)
        {
            b=a->next;
            a->next=b->next;
            tail->next=b;
            b->next=NULL;
            tail=b;
            a=a->next;
        }
        return head;
    }
};

判断是否为回文链表

迭代法

迭代法的思路是先把链表的前一半反转,然后用两个指针分别遍历前后一半。这个做法还需要注意链表长度的奇偶,相对比较复杂。

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        if(head==nullptr||head->next==nullptr)
        return true;
        ListNode *fast,*slow,*head1,*head2;
        fast=head->next;slow=head;
        bool isodd=false;
        while(fast->next!=nullptr)
        {
            fast=fast->next;
            if(fast->next==nullptr)
            {isodd=true; break;}
            fast=fast->next;
            slow=slow->next;
        }
        if(slow==head)
        {
            if(slow->val==fast->val)
            return true;
            else
            return false;
        }
        if(isodd)
            head2=slow->next->next;
        else
            head2=slow->next;//找到链表的一半
        ListNode *a,*b;
        a=head;
        while(a!=slow)
        {
            b=a->next;
            a->next=slow->next;
            slow->next=a;
            a=b;
        }//前一半反转
        head1=slow;
        while(head2!=nullptr)
        {
            if(head1->val!=head2->val)
            return false;
            head1=head1->next;
            head2=head2->next;
        }
        return true;
    }
};

递归法

  • 设置一个全局变量,用于保存头节点的引用。
  • 递归到最底层,然后返回的时候,和头节点同时比较(头节点从前往后,他从后往前)
class Solution {
    ListNode first;
    public boolean isPalindrome(ListNode head) {
        // 使用递归的方法
        first = head;
        return helper(head);
    }

    public boolean helper(ListNode last){
        if(last != null){
            // 一直递归下去
            if(!helper(last.next)) return false;
            // 出递归的时候,正好是从后往前
            if(last.val != first.val) return false;
            first = first.next;
        }
        return true;
    }
}

递归法非常精妙地利用了递归传递的条件,即链表为空或者这一层的头尾元素相同,把这个条件变成bool变量,通过两个判断语句完成了这个过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值