力扣刷题Day3

链表理论基础

链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。

链接的入口节点称为链表的头结点也就是head。

链表的类型:

单链表:

单链表中的指针域只能指向节点的下一个节点。

链表1

双链表:

双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。

双链表 既可以向前查询也可以向后查询。

链表2

循环链表:

链表首尾相连。

链表4 

链表的储存方式

链表在内存中不是连续分布的。

链表是通过指针域的指针链接在内存中各个节点。

所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。

链表3

这个链表起始节点为2, 终止节点为7, 各个节点分布在内存的不同地址空间上,通过指针串联在一起。

链表的构造(java):

public class ListNode {
         int val;
         ListNode next;
         ListNode() {} //无参
         ListNode(int val) { this.val = val; }//一个参数
         //两个参数
         ListNode(int val, ListNode next) { this.val = val; this.next = next;}
    }

删除节点: 

链表-删除节点

只要将C节点的next指针 指向E节点就可以了。

那有同学说了,D节点不是依然存留在内存里么?只不过是没有在这个链表里而已。

是这样的,所以在C++里最好是再手动释放这个D节点,释放这块内存。

其他语言例如Java、Python,就有自己的内存回收机制,就不用自己手动释放了。

添加节点:

链表-添加节点

复杂度为O(1)

 数组和链表的对比:

链表-链表与数据性能对比

数组在定义的时候,长度就是固定的,如果想改动数组的长度,就需要重新定义一个新的数组。

链表的长度可以是不固定的,并且可以动态增删, 适合数据量不固定,频繁增删,较少查询的场景。

参考资料:代码随想录

 203. 移除链表元素

题目:力扣

涉及如下链表操作的两种方式:

  • 直接使用原来的链表来进行删除操作。
  • 设置一个虚拟头结点在进行删除操作。

这里采用的是第二种方式:

public ListNode removeElements(ListNode head, int val) {
        ListNode result = new ListNode(0);//虚拟结点
        result.next = head;
        ListNode temp = result;//在head前一位,如果head为val需要删除的时候方便操作;
        //如果不用虚拟节点,那么就需要对头节点进行判断,是否为空?是否为val然后将头节点移动
        //head = head.next

        while(head != null){
            if(head.val == val){
                head = head.next;
                temp.next = head;//如果不采用虚拟节点可以这么写:temp.next.next = head
            }else{
                head = head.next;
                temp = temp.next;
            }
        }

        return result.next;
    }

参考资料:

代码随想录

707.设计链表

题目:力扣

自己写了好几次测试用例都出问题 ,研究了下发现在设置变量的时候直接采用了head节点,但是在进行删除、添加头节点操作的时候很容易出现问题。于是改成用虚拟节点。

刚开始打算完全模拟链表,但是index是否有效很难判断,每次都需要遍历并且循环容易出问题,因此增加了size变量。

ps:需要注意边界问题

class Node{
    int val;
    Node next;

    Node(){};
    Node(int val){this.val = val;}
}

class MyLinkedList {

    Node head;//虚拟节点
    //考虑到删除节点 - 设置虚拟节点比较方便
    int size;

    public MyLinkedList() {
        head = new Node(0);
        size = 0;
    }
    
    public int get(int index) {
        if(index <0 || index >= size){ //index = 0, size = 1
            return -1;
        }

        Node cur = head;
        for(int i=0; i<=index; i++){ //注意从虚拟指针开始遍历
            cur = cur.next;
        }

        return cur.val;

    }
    
    public void addAtHead(int val) {
        addAtIndex(0, val);
    }
    
    public void addAtTail(int val) {
        addAtIndex(size, val);
    }
    
    public void addAtIndex(int index, int val) {
        if(index > size) return;
        if(index < 0) index = 0;

        Node cur = head;
        for(int i=0; i= size){
            return;
        }

        Node cur = head;
        for(int i=0; i

参考资料:代码随想录

206. 翻转链表

题目:力扣

很容易就想到通过迭代的方式,在head前面增加一个null节点,先储存head的下一个节点,然后将head的next指针指向null节点,然后将head向后移动,依次迭代改变链表的指针方向,最后直接输出原来最后一个节点,作为头节点。

但是在实现的时候需要注意循环的写法:

public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        while(head != null){ //这里不能使用head.next != null,这样最后一个节点遍历不到
            head = head.next;//储存head下一个节点
            cur.next = pre;//换方向
            pre = cur;//移位
            cur = head;//移位
        }

        return pre;//head为null了,pre在head前一位,也就是最后一个节点
    }

还可以使用递归来实现。

参考资料:代码随想录

你可能感兴趣的:(leetcode刷题,leetcode,链表,数据结构)