代码训练营 DAY3 打卡

  本文由GarfieldTheOldCat原创,转载请标明dekkyandlappy-CSDN博客

最近忙着学校的各种毕业事项,鸽了几天,昨天参加完毕业典礼之后开始赶进度,以后争取一天一至二更。

今天进入链表部分了

基础

链表在之前数据结构的学习中接触过,是一种需要部分自行定义的数据结构。链表通过指针将不同的节点串联到一起。对于单链表,每个节点由两部分构成,其中一部分用于存储数据,另一部分用于存储下一个节点的指针。第一个节点称为头节点head,通过访问head,可以遍历整个链表;最后一个节点指向空(对java而言是null,对python而言是None)。

除此之外,还有双链表(每个节点由三部分构成,包括数据和指向前后节点的两个指针)和循环链表(首尾相连)。

与数组不同,链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。链表适合数据量经常发生变化(增加或删除),但是查询比较少的场合。

单表定义:

java

package Data_structure;

public class ListNode{
    public int val;
    public ListNode next;

    public ListNode() {}
    public ListNode(int val) { this.val = val; }
    public ListNode(int val, ListNode next) { this.val = val; this.next = next; }

}

python(不用类名初始化)

class ListNode:
    def __init__(self,val=None,next=None):
        self.val=val
        self.next=next

移除链表元素

链表元素是通过指针相连接的,因此,如果想要删除某个元素,只要将指向此元素的指针指向此元素的下一个元素即可,即前一个元素previous的next=previous的next的next。

这个过程看着比较简单,但是有一下两个难点:

  1. 如果需要移除的元素为链表的第一个元素(头节点),此时不存在上一个元素;
  2. 如果需要移除的元素为链表最后一个元素,此时下个元素为null

为了解决这些问题,有两种方法:

在原链表上直接操作:先检查头节点是否要删除,若要删除则head=head.next,再遍历剩下的链表,完成删除操作,到最后一个元素时停止遍历。

虚拟头节点:引入一个虚拟的头节点Dummyhead,使其next为原始的head,直接遍历剩下的链表,完成删除操作,到最后一个元素时停止遍历。注意:此方法返回的是虚拟头节点Dummyhead的next

lc23:203. 移除链表元素

思路和刚才介绍的一样

首先看直接操作的实现:

java版

// 不使用虚拟头节点
    public ListNode removeElements_0(ListNode head, int val) {
        // 处理head节点

        while(head !=null && head.val==val){
            head=head.next;
        }
        if (head == null) return head;
        ListNode current=head;
        if (current.next ==null) return head;
        while(current.next.next != null){
            ListNode next =current.next;
            if (next.val==val) current.next=current.next.next;
            else current=current.next;
        }
        if (current.next.val ==val) current.next=null;
        return head;
    }

这个是我自己按照思路自己写的代码,仍然比较啰嗦,按照题解进行了精简:

    // 参考
    public ListNode removeElements_1(ListNode head, int val) {
        while(head!=null && head.val==val){
            head = head.next;
        }
        ListNode curr = head;
        while(curr!=null){
            while(curr.next!=null && curr.next.val == val){
                curr.next = curr.next.next;
            }
            curr = curr.next;
        }
        return head;
    }

精简之后的代码不需要对尾节点做额外的处理。

python版:

    # 原始链表
    def removeElements(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        while head is not None and head.val == val:
            head=head.next

        current=head
        while current is not None:
            while current.next is not None and current.next.val ==val:
                current.next=current.next.next
            current=current.next
        return head

python在判断节点是否为空时可以不用==,而用 is None/is not None

再实现使用虚拟头节点的方法:

Java版

// 虚拟头节点
    public ListNode removeElements(ListNode head, int val) {
        ListNode DummyHead=new ListNode();
        DummyHead.next=head;
        ListNode current=DummyHead;

        while(current !=null){
            while(current.next != null && current.next.val ==val){
                current.next =current.next.next;
            }
            current=current.next;
        }
        return DummyHead.next;
    }

python版:

python第一次实现报错:

class Solution(object):
    # 虚拟头节点,错误
    def removeElements_0(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        Dummyhead=ListNode(None,head)
        current=Dummyhead
        while current.next is not None:
            if current.next.val == val:
                current.next=current.next.next
            current=current.next

        return Dummyhead.next

检查发现遗漏了对current是否为None的判断:

# 虚拟头节点
    def removeElements_1(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        Dummyhead=ListNode(None,head)
        current=Dummyhead
        while current is not None:
            while current.next is not None and current.next.val ==val:
                current.next=current.next.next
            current=current.next

        return Dummyhead.next

改正后就可以了。

设计链表

包括链表的增,删,返回下标等功能的实现。

lc707:707. 设计链表

先上java:

package integer_array_round1.day3;
import Data_structure.ListNode;
class MyLinkedList_v1 {
    private ListNode Dummyhead;

    public MyLinkedList_v1() {
        this.Dummyhead = new ListNode();
        this.Dummyhead.next=null;
    }

    public int get(int index) {
        int pos=-1;
        ListNode current=this.Dummyhead;
        while(current != null){
            if( pos ==index) return current.val;
            pos++;
            current=current.next;
        }
        return -1;

    }

    public void addAtHead(int val) {
        ListNode head_adder=new ListNode(val,this.Dummyhead.next);
        ListNode current=this.Dummyhead;
        this.Dummyhead.next=head_adder;

    }

    public void addAtTail(int val) {
        ListNode tail_adder=new ListNode(val,null);
        ListNode current=this.Dummyhead;
        while(current.next != null) current=current.next;
        current.next=tail_adder;
    }

    public void addAtIndex(int index, int val) {
        ListNode index_adder=new ListNode(val);
        int pos=-1;
        ListNode current=this.Dummyhead;
        while(current != null){
            if( pos ==index-1){
                index_adder.next=current.next;
                current.next=index_adder;
                return;
            }
            pos++;
            current=current.next;
        }
        return;
    }

    public void deleteAtIndex(int index) {
        int pos=-1;
        ListNode current=this.Dummyhead;
        while(current != null){
            if( pos ==index-1){
                if(current.next != null){
                    current.next=current.next.next;
                    return;
                }
            }
            pos++;
            current=current.next;
        }
        return;

    }
}

观察题解时发现addAtHead和addAtTail都可以由addAtIndex实现,在python版本里完整了这种方法:

from Data_structure.ListNode import ListNode


class MyLinkedList(object):

    def __init__(self):
        self.DummyHead = ListNode()
        self.size = 0

    def get(self, index):
        """
        :type index: int
        :rtype: int
        """
        if index < 0 or index >= self.size:
            return -1
        else:
            current = self.DummyHead
            count = 0
            while count <= index:
                current = current.next
                count = count + 1
            return current.val

    def addAtHead(self, val):
        """
        :type val: int
        :rtype: None
        """
        return self.addAtIndex(0, val)

    def addAtTail(self, val):
        """
        :type val: int
        :rtype: None
        """
        return self.addAtIndex(self.size, val)

    def addAtIndex(self, index, val):
        """
        :type index: int
        :type val: int
        :rtype: None
        """
        adder = ListNode(val=val)
        if index < 0 or index > self.size:
            return
        else:
            current = self.DummyHead
            count = 0
            while count < index:
                current = current.next
                count = count + 1
            if index == self.size:
                current.next = adder
                adder.next = None
            else:
                adder.next = current.next
                current.next = adder
            self.size = self.size + 1
            return

    def deleteAtIndex(self, index):
        """
        :type index: int
        :rtype: None
        """
        if index < 0 or index >= self.size:
            return
        else:
            current = self.DummyHead
            count = 0
            while count < index:
                current = current.next
                count = count + 1
            if index == self.size:
                current.next = None
            else:
                current.next = current.next.next
            self.size = self.size - 1
            return

# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)

TODO:这题第一次写错了很多个地方,之后有空再试试;另外,也可以试试不用虚拟头节点的实现。

翻转链表

分为两种做法:

首先是双指针(实际需要三个指针):初始current指针指向head,previous和temp均置为空。流程如下:

  1. temp置为current的next
  2. current的next指向previous
  3. previous置为current
  4. current置为temp
  5. 重复以上步骤,直到current为空,此时previous为新的头节点

注意:3和4的顺序不可以颠倒,否则无法正常移动。

另一种做法是递归,大体逻辑和双指针类似,不过循环改为递归实现。

lc206:206. 反转链表

首先是双指针:

java

第一个版本报错:Error - Found cycle in the ListNode...

    // 双指针法,错误Error - Found cycle in the ListNode...
    public ListNode reverseList_0(ListNode head) {
        ListNode current=head;
        ListNode previous = new ListNode();
        previous.next=current;
        ListNode temp=head;

        while (current != null){
            temp=current.next;
            current.next=previous;
            previous=previous.next;
            current=temp;
        }
        return previous;
    }

检查发现,current.next = previous;一行与previous.next=current;构成了循环,对其修改如下:
 

// 双指针法,错误
    public ListNode reverseList_1(ListNode head) {
        ListNode current=head;
        ListNode previous = new ListNode();
        ListNode temp=head;

        while (current != null){
            temp=current.next;
            current.next=previous;
            previous=current;
            current=temp;
        }
        return previous;
    }

去除了previous.next=current;但是仍然报错,检查发现在于previous的定义上:

我们需要的previous初始的null是指previous初始为空指针,而我的语句导致previous初始时为一个空的ListNode对象而不是空指针。

 最终改正如下:

// 双指针法,
    public ListNode reverseList_2(ListNode head) {
        ListNode current=head;
        ListNode previous = null;
        ListNode temp=head;

        while (current != null){
            temp=current.next;
            current.next=previous;
            previous=current;
            current=temp;
        }
        return previous;
    }

python版本:

    # 双指针
    def reverseList_0(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        current = head
        previous = None  # None和 ListNode()区别?
        while current is not None:
            temp = current.next
            current.next = previous
            previous = current
            current = temp
        return previous

搞懂了双指针之后,实现递归就很容易了。

java:

    // 递归
    // 主函数
    public ListNode reverseList(ListNode head) {
        return reverse(null,head);
    }
    // 递归函数
    public ListNode reverse_0(ListNode previous, ListNode current){
        if(current == null) return previous;
        else{
            ListNode temp = current.next;
            current.next=previous;
            previous=current;
            current=temp;
            return reverse_0(previous,current);
        }
    }

对递归部分的写法进行优化:
 

 // 递归函数,写法优化
    public ListNode reverse(ListNode previous, ListNode current){
        if(current == null) return previous;
        else{
            ListNode temp = current.next;
            current.next=previous;
            return reverse(current,temp);
        }
    }

python

    # 递归
    # 递归函数
    def reverse(self, previous, current):
        if current is None: return previous
        temp = current.next
        current.next = previous
        return self.reverse(current, temp)

    # 主函数
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        return self.reverse(None, head)

注意写递归时不要忘记写递归中止条件!

 本文由GarfieldTheOldCat原创,转载请标明

 本文由GarfieldTheOldCat原创,转载请标明

总结

今天学习了链表这一种数据结构,其增删简单,遍历复杂,适合经常修改而较少遍历的数据

移除链表元素要注意头节点和尾节点

反转链表使用双指针或递归

Todo

lc707第一次写有错,有空复习一下,再试试别的写法

再想想ListNode previous = null;和ListNode previous = new ListNode();的区别。

   本文由GarfieldTheOldCat原创,转载请标明dekkyandlappy-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值