算法|day3 链表:链表的构造、设计、反转链表(断链处理)

1. 构建链表

1. 要点

  • 构造链表的三种方式:无参、单参数、双参数
  • 需要明白java中引用的形式,了解cur = head,cur改变,head也改变,这种引用的概念,类似文件的快捷方式,通过引用我们就可以找到那个对象,通过引用,我们来遍历、修改链表
  • 构造的方式有点像递归,可以联想到可以用递归创建链表

2. 代码

public class ListNode{
	int val;
	ListNode next;对象 :引用下一个节点对象。在Java中没有指针的概念,Java中的引用和C语言的指针类似
	
	public ListNode(){}
	
	
	pulic ListNode(int val){
		this.val = val;
	}
	public ListNode(int val,ListNode next){
		this.val = val;
		this.next = next;
	}
}

2. 遍历链表:移除节点

链表的遍历不同于数组

  • 初始条件不是i=0,而是curNode = head(如有虚拟头节点dummyHead,则是curNode=dummy.next)
  • 终止条件不是i<nums.length,而是curNode != null
  • 递增条件不是i++,而是curNode=curNode.next
    明白这些,使用for或者while遍历链表均可

1. 题目:

  1. 移除链表元素

https://leetcode.cn/problems/remove-linked-list-elements/

2. 初提结果及问题

问题在虚拟头节点使用,链表增删改查操作真正对象的理解以及链表遍历的细节三个方面

  • 注意虚拟头节点对于链表的增加、删除及插入的帮助,以及其定义、返回
    • 对虚拟节点的定义方式不熟悉 ListNode dummy = new ListNode(-1,head)
    • 如果创建虚拟节点,返回值应该是return dummy.next,而不是head,因为head可能会被删除,插入等,dummy.next才是真正头节点
  • 程序报错cannot read field next because local4 is null,如果if满足条件,cur.next会更新到下下个(下面图中返回值也错误)
    程序报错cannot read field next because local4 is null,如果if满足条件,cur.next会更新到下下个
  • 删除节点本质是对前一个节点操作,因此终止条件、递增对象都应该是cur.next(单链表中这么操作,双链表不用)

3.设计链表

1. 题目

  1. 设计链表
    https://leetcode.cn/problems/design-linked-list/description/

这道题分为基于index的增删查以及最前后插入。由于插入节点是对插入位置前一个节点操作,因此建立在最后一个插入不需要单独操作,对最前一个建立虚拟头节点即可。
根据index增删查

  • 获取链表第index个节点的数值
  • 在链表第index个节点前面插入一个节点
  • 删除链表的第index个节点

头尾两个插入可以合并在根据index增里面

  • 在链表的最前面插入一个节点
  • 在链表的最后面插入一个节点

2. 初次提交结果与问题

这道题几个方法的结果都是依赖的,因此一个写错就可能报错 next属性不存在等,出现节点为空的情况

- 难点总结为:
- 学会通过嵌套类构建新类;
- 加强理解单链表操作都是根据其前驱节点操作(延申到虚拟头节点的使用、遍历条件、下标的初始值);
- 仔细读题判断不同操作下输入index的实际范围、合法范围及非法操作返回值

  • 具体失误的问题是:
    • 不知道要在MyLinkedList类里面构造链表后,再在MyLinkedList里面初始化一个链表对象(既一个节点都没有(或者只有一个虚拟节点)的对象)
    • 在构造函数中赋值和直接给成员变量赋值的区别(https://blog.csdn.net/qq_43019319/article/details/107196434)
    • 需要注意的是,删和查都是需要有链表长度的判断,因此需要加入size属性
    • 在链表的最前面、最后面插入一个节点,都可以转为对于index定值的操作
    • 要学会看输入值的范围,比如这里index恒大于等于0,因此不用写小于0的判断
    • 添加到末尾是 直接加到index 为size的(实际下标大1)
    • 注意题目中描述,查的index可输入范围和写的index范围不一样,写的index范围是可以比实际下标大1
    • curIndex忘记要++
    • 由于很容易出错,后续改了半天才开始用输出来debug,方法如下,输出在dele和add里面写,注意写法。这样可以很清楚的看出目前链表里面有哪些值,排查是那一步写错了
    public void addAtIndex(int index, int val) {
        if(index > size){return;}

        ListNode cur = dummy; //循环从虚拟头节点开始,索引从-1开始,
        int curIndex = -1;
        while(cur.next != null && (curIndex+1) != index){
            cur = cur.next;
            curIndex ++;
        }
        ListNode addNode = new ListNode(val,cur.next);
        cur.next = addNode;
        size ++;

		//测试当前链表值的方法
        ListNode test = dummy.next;
        while(test != null){
            System.out.print(test.val +" ");
            test=test.next;
        }
        System.out.println();
    }

3. 题解

思路为:分析拆解归类问题→根据链表特点编写普适遍历条件→调整一些边界条件的细节,合理使用print来debug

//单链表写法
class MyLinkedList {
    // 构造链表
    class ListNode{
        int val;
        ListNode next;
        public ListNode(){}
        public ListNode(int val){ this.val = val;}
        public ListNode(int val,ListNode next){
            this.val = val;
            this.next = next;
        }
    }

    ListNode dummy;
    int size;
    public MyLinkedList() {
        dummy = new ListNode(-1,null);
        size = 0;
    }

    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;}

        ListNode cur = dummy; //循环从虚拟头节点开始,索引从-1开始,
        int curIndex = -1;
        while(cur.next != null && (curIndex+1) != index){
            cur = cur.next;
            curIndex ++;
        }
        ListNode addNode = new ListNode(val,cur.next);
        cur.next = addNode;
        size ++;

		//测试当前链表值的方法
        // ListNode test = dummy.next;
        // while(test != null){
        //     System.out.print(test.val +" ");
        //     test=test.next;
        // }
        // System.out.println();
    }

    public int get(int index) {
        if(index >= size){return -1;}

        ListNode cur = dummy;
        int curIndex = -1;
        while(cur.next != null && (curIndex + 1) != index){
            cur = cur.next;
            curIndex ++;
        }
        return cur.next.val;
    }

    public void deleteAtIndex(int index) {
        if(index >= size){return;}

        ListNode cur = dummy;
        int curIndex= -1;
        while(cur.next != null && (curIndex + 1) != index){
            cur = cur.next;
            curIndex ++;
        }
        cur.next = cur.next.next;
        size --;
        //测试当前链表值的方法
        // ListNode test = dummy.next;
        // while(test != null){
        //     System.out.print(test.val +" ");
        //     test=test.next;
        // }
        // System.out.println();
}

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

3. 链表断链处理的例子:反转链表

这题可以深入理解链表中引用(指针)的概念和用法。
在做完两两交换节点后,更加明白这道题与其区别:一个是改变方向后断链了(用双指针+临时指针处理),一个是改变方向后没有断链(单指针即可)

1. 题目:

  1. 反转链表

https://leetcode.cn/problems/reverse-linked-list/

2. 初提结果及问题

  • 由于更新循环时,需要pre和cur都移动一位:
    • pre根据cur当前值移动,
    • cur因为.next已经被更新了,需要创建的临时变量tmp来存当前节点的下一个用于cur的移动进入下一个循环;
  • 没有注意最后一个前驱节点才是头节点 应该return pre;

3.题解

/**
 * Definition for singly-linked list.
 * 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; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
    	//双指针
        ListNode pre = null;
        ListNode cur = head;
        while(cur != null){
            //1. 存下一个
            ListNode temp = cur.next;
            //2. 指针翻转(翻转后得到的一个我们想要的cur)
            cur.next = pre;
            // 3. 更新
            pre= cur;
            cur = temp;
        }
        return pre;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值