Leetcode解题系列(3)

leetcode解题系列(3)

141 环形链表

题目叙述:给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数pos来表示链表尾连接到链表中的位置(索引从0开始)。 如果pos是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true。否则,返回false 。

思路: 遇到链表的问题,首先想到的就是双指针的方法是否可以解决:
对于这道题,我们可以设置快慢指针,一个循环中,快指针走两个节点,慢指针走一个节点;
若该链表是环形链表,则容易证明,快指针迟早会比慢指针快上一圈,两个指针就可以指向同一个节点,输出true;
若非环形链表,也可以很容易想到,快指针会首先到达链表尾部,输出false即可;
代码如下:

class Solution {
public:
    bool hasCycle(ListNode *head) {
    ListNode* fast=head;
    ListNode* slow=head;
    if((head==nullptr)||(head->next==nullptr))
    return false;
    while((fast!=nullptr)&&(fast->next!=nullptr)){
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
        return true;
    }
    return false;
    }
};

这个简陋的代码是可以跑通的。
这种做法的空间复杂度是O(1),因为我们只额外使用了两个指针的空间;时间复杂度是O(N),N是链表节点的数量。

另外一种解法: 使用哈希表,首先我们定义一个hashset集合,然后我们去遍历这个链表,将
链表节点按顺序存储到set集合中。
如果存入失败,证明该元素已被存入,也就构成了环形。
代码如下:

class Solution {
public:
    bool hasCycle(ListNode *head) {
        if (!head || !head->next) return false;

        unordered_set<ListNode*> hashtable;
        ListNode *p = head;

        while (p) {
            if (hashtable.count(p))
                return true;
            hashtable.insert(p);
            p = p->next;
        }
        return false;
    }
};

直接使用哈希表中的函数count判断是否存在相同元素,感觉有点bug,因为不知道是怎么实现的。
哈希表的一些使用:
创建:unordered_set<ListNode*> hashtable;
判断是否已有相同元素:hashtable.count§;
插入新的元素:hashtable.insert§;

回文链表

题目描述: 请判断一个链表是否为回文链表。
示例 1:
输入: 1->2 输出: false
示例 2:
输入: 1->2->2->1 输出: true

想到最简单的方法就是设置两个指针,一个从头开始搜索,一个从链表尾部开始搜索,比较两者元素的值,若相同则继续比较,若不同则返回false;当两者指向相同元素或者begin->next=end则输出true;但这个方法要注意的是必须先要把链表转换成指针,否则end是没有办法往前移动的。
代码如下:

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        ListNode*a=head;
        int i=0;int n=0;
        vector<int>b;
        if((head==nullptr)||(head->next==nullptr))return true;
        for(i=0;a->next!=nullptr;i++){
            b.insert(b.end(),a->val);
            a=a->next;
        }
        b.insert(b.end(),a->val);
       
        while(n<int(b.size()/2.0)){
        if(b[n]!=b[b.size()-n-1])return false;
        n++;
        }
        return true;
    }
};

没想到这么简单个程序我写了这么久。。。主要是vector动态数组已经快忘记了,还有就是leetcode不让调试,想了输出的方法自己调试的。
给我的一个教训就是运行前一定要仔细审视一下循环体内的东西写对了没有,有没有超过数组范围,最后的元素对不对。
这个方法是通过了,但是还不够高级,看看另外的方法:
用递归的方法也可以做到,但只是写法比较秀,时间复杂度和空间复杂度上并没有什么优势,故不再此赘述。
还有个方法:
快慢指针:我们也可以用快慢指针来实现这个功能,具体就是,我们先利用
快慢指针,找到链表的中间位置。然后将链表一分为二,并反转后半部分链表。然后对两个链表进
行比对,如果每个节点的值都相等。那么就证明这个链表是回文链表,
我们再把链表恢复原样。
要找到中间节点,只要使快指针每次走两步,慢指针走一步,当快指针到达链表末尾时,慢指针就到中点了,然后将中点之后的所有节点入栈;
依次出栈和head后的元素相比较即可;
代码如下:

class Solution {

    public boolean isPalindrome(ListNode head) {

        if (head == null || head.next == null) return true;

        Stack<ListNode> stack = new Stack<>();
        ListNode slow = head;
        ListNode fast = head;

        while (fast != null && fast.next != null && fast.next.next != null) {

            slow = slow.next;
            fast = fast.next.next;
        }

        ListNode mid = slow;

        while (mid.next != null) {

            stack.push(mid.next);
            mid = mid.next;
        }

        ListNode p = head;
        while (!stack.isEmpty()) {

            if (p.val != stack.pop().val) {

                return false;
            }
            p = p.next;
        }
        return true;
    }
}

链表中倒数第K个元素

题目描述: 返回链表中倒数第k个元素为首的链表,
示例:1->2->3->4->5,2
返回4->5
方法:这个题可以采用双指针进行实现。首先我们定义两个指针former
和latter,并让他们都指向头节点。然后我们先让former指针先进行移动K次。然后两个指针在同
时移动。这样当我们的former移动到为空的位置的时候,我们的latter
节点正好在倒数第K个节点上.
代码:

class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        ListNode* former=head;
        ListNode* latter=head;
        while(k!=1){
            latter=latter->next;
            k--;
        }
        while(latter->next){
            latter=latter->next;
            former=former->next;
        }
        return former;
    }
};

这个方法是非常巧妙的,nono了xdm!
可以总结一下,链表中的问题,很多时候需要用到双指针,主要是因为链表这种东西没有数组的索引,不是很方便,所以要用两个指针之间的关系。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值