【数据结构_5】链表(模拟实现以及leetcode上链表相关的题目)

书接上文,继续编写链表的功能

4.链表的中间插入

在链表中,本身是没有下标这样的概念的,不像顺序表,顺序表根据下标访问元素,O(1)复杂度。链表需要遍历之后找到正确的位置才能进行插入,为O(N)复杂度。

但是这件事在JAVA中是个例外,JAVA标准库LinkedList引入了下标概念。

java 复制代码
    public int size(){
        int size =0;
        for(Node cur = head; cur.next!=null;cur = cur.next){
            size++;
        }
        return size;
    }

    //4.中间插入的操作
    public void add(int index,String value){
       int size = size();
        //1.首先判断index是否合法
        if(index<0 || index >size){
            throw  new RuntimeException("下标超出范围");
        }
        //1.首先要找到index-1的prev节点
        Node prev = head;
        
        //上述代码出现了一个问题:如果是头插,那么就会导致循环无法进去,那么prev是第一个节点,插入的永远是下标为1的地方
        //*特殊情况需要特殊考虑
        if(index ==0){
            addFirst(value);
            return;
        }
        
        for (int i = 0; i < index-1; i++) {
            prev = prev.next;
        }
        //3.此时进行修改操作
        Node newNode = new Node(value);
        newNode.next = prev. next;
        prev.next = newNode;
    }
复制代码
//5.看某个元素是否被包含在链表当中
java 复制代码
 public boolean contains(String value){

        for(Node cur = head;cur.next != null;cur = cur.next){
            if(cur.value.equals(value)){
                return true;
            }
        }
        return false;
    }
复制代码
//6.找到了就返回index
java 复制代码
 public int indexOf(String value){
        int index =0;
        for(Node cur = head;cur.next != null;cur = cur.next){
            if(cur.value.equals(value)){
                return index;
            }else {
                index++;
            }
        }
        return -1;
    }
复制代码
//7.根据下标删除
java 复制代码
    //7.根据下标删除
    public void remove( int index){

        int size = size();



        //1.首先要判断index的值是否合法
        if( index <0 || index >=size){
            throw new RuntimeException("下标越界");
        }


        //*要考虑特殊的删除头节点
        if(index ==0){
            head = head.next;
            return ;
        }


        //2.其次要找到上一个节点
        Node prev = head;
        for (int i = 0; i < index-1; i++) {
            prev = prev.next;
        }


        //3.然后要进行删除操作
        prev.next = prev.next.next;

    }
复制代码
//8.根据值来删除
java 复制代码
    public void remove(String value){

        //还要考虑空链表的情况
        if(head == null){
            return;
        }
        //有关添加删除操作都要考虑前一个节点 所以每次创建的都是prev
        Node prev = head;
        for(;prev!=null;prev= prev.next){
            if(prev.next!= null&&prev.next.equals(value)){
                //找到了
                break;
            }
        }
        //出来之后有两种情况 一种是找到了value 另一种是遍历完了都没有找到value
        if(prev == null){
            return;
        }else{
            //找到了,进行删除操作
            Node cur = prev.next;
            prev.next = cur.next;
        }

    }

//9.全部删除的clear操作

java 复制代码
    //9.clear()
    public void clear(){
        head = null;
    }

至此,LinkedList基本写成了

总体:

java 复制代码
package LinkedList;

//要想实现链表的基本功能,首先要表示链表的一个节点
//对于节点这个类来说,他的本身功能单一,比较简单
//如果高一些get set 方法 ,后续代码就会显得很难看

class Node{
    public String value;
    //节点保存的值
    public Node next;
    //这个节点的下一个元素
    public Node(String value) {
        this.value = value;
        this.next = null;
    }
    //当我们创建一个Node的时候,就创建好了链表的头节点,此时链表头节点的值可以确定,且尚未含有下一个节点
}
//这是一个单链表的节点 双向链表还需要一个prev
public class MyLinkedList {
    //把链表的头节点表示出来,此时整个链表就都能被获取到了
    //此处不包含傀儡系欸但,head== null 的时候表示空的链表
    private Node head = null;
    //1.链表的头插操作
    public void addFirst(String value){
        Node newNode = new Node(value);
        newNode.next =head;
        head = newNode;
        //head只是一个引用类型!!!

    }
    //2.遍历链表的操作
    public String toString(){

        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[");
        for (Node cur = head;cur!= null; cur= cur.next) {

            stringBuilder.append(cur.value);
            if(cur.next!= null) {
                stringBuilder.append(",");
            }
        }
        stringBuilder.append("]");
        return stringBuilder.toString();
    }
    //3.尾插操作
    public void addLast( String value){
        //1.首先找到最后一个节点
        Node tail = head;
        //*还要考虑特殊的情况
        if(head == null){
            Node node = new Node(value);
            head = node;
            return;
        }
        for (;tail.next!=null; tail = tail.next) {
            if(tail.next==null){
                break;
            }
        }
        //此时的tail也就是我们要找的最后一个节点
        Node node = new Node(value);
        tail.next = node;
        node.next = null;

    }
    public int size(){
        int size =0;
        for(Node cur = head; cur.next!=null;cur = cur.next){
            size++;
        }
        return size;
    }
    //4.中间插入的操作
    public void add(int index,String value){
       int size = size();
        //1.首先判断index是否合法
        if(index<0 || index >size){
            throw  new RuntimeException("下标超出范围");
        }
        //1.首先要找到index-1的prev节点
        Node prev = head;
        //上述代码出现了一个问题:如果是头插,那么就会导致循环无法进去,那么prev是第一个节点,插入的永远是下标为1的地方
        //*特殊情况需要特殊考虑
        if(index ==0){
            addFirst(value);
            return;
        }
        for (int i = 0; i < index-1; i++) {
            prev = prev.next;
        }
        //3.此时进行修改操作
        Node newNode = new Node(value);
        newNode.next = prev. next;
        prev.next = newNode;
    }
    //5.看某个元素是否被包含在链表当中
    public boolean contains(String value){

        for(Node cur = head;cur != null;cur = cur.next){
            if(cur.value.equals(value)){
                return true;
            }
        }
        return false;
    }
    //6.找到了就返回index
    public int indexOf(String value){
        int index =0;
        for(Node cur = head;cur != null;cur = cur.next){
            if(cur.value.equals(value)){
                return index;
            }else {
                index++;
            }
        }
        return -1;
    }
    //7.根据下标删除
    public void remove( int index){
        int size = size();
        //1.首先要判断index的值是否合法
        if( index <0 || index >=size){
            throw new RuntimeException("下标越界");
        }
        //*要考虑特殊的删除头节点
        if(index ==0){
            head = head.next;
            return ;
        }
        //2.其次要找到上一个节点
        Node prev = head;
        for (int i = 0; i < index-1; i++) {
            prev = prev.next;
        }
        //3.然后要进行删除操作
        prev.next = prev.next.next;
    }

    //8.根据值来删除
    public void remove(String value){
        //还要考虑空链表的情况
        if(head == null){
            return;
        }
        //有关添加删除操作都要考虑前一个节点 所以每次创建的都是prev
        Node prev = head;
        for(;prev!=null;prev= prev.next){
            if(prev.next!= null&&prev.next.equals(value)){
                //找到了
                break;
            }
        }
        //出来之后有两种情况 一种是找到了value 另一种是遍历完了都没有找到value
        if(prev == null){
            return;
        }else{
            //找到了,进行删除操作
            Node cur = prev.next;
            prev.next = cur.next;
        }
    }
    //9.clear()
    public void clear(){
        head = null;
    }
}
java 复制代码
class Solution {
    public ListNode removeElements(ListNode head, int val) {
                //1.首先判断链表是否为空
        if(head == null){
            return null;
        }
        //2.利用循环删除每一个值为val的元素,但是是从head后面开始删除的!
        ListNode prev = head;
        ListNode cur = prev.next;
        while(cur!=null){
            if(cur.val == val){
                //就触发删除操作
                prev.next = cur.next;
                //还要将cur进行后置
                cur = prev.next;
            }else{
                prev = cur;
                cur =cur.next;
            }
        }
        //cur == null 再判定开头的元素
        if(head.val==val){
            head = head.next;
        }
        return head;
    }

    }

1.这道题的要点就在于头节点的删除,如果head=[7,7,7,7],我们就先不管头节点,先把后面值等于val的节点删除,然后循环出来再去考虑头节点。

现在要得到这个链表的翻转链表

思路:分别设置三个引用变量:prev,cur,next。使三者遍历整个链表,按照如下图所示的操作完成链表的翻转。

*为什么一定要设置三个,而不能只使用cur?

因为在完成翻转操作之后,我们还想让循环继续,但是此时cur.next=prev,所以我们要通过next这个引用变量为我们指明前方的道路

也就是说,pev=cur;cur = next; next = next.next;

一直到cur == null,然后跳出循环

java 复制代码
class Solution {
    public ListNode reverseList(ListNode head) {
     //1.首先判断链表是否为空
     if(head == null){
        return null;
     }   
     //2.如果链表里只含有一个元素,那么翻转还是不反转没有任何影响
     if(head.next == null){
        return head;
     }
     //3.来处理一般情况
     //首先要创建三个引用变量
     ListNode prev = null;
     ListNode cur = head;
     ListNode next = cur.next;
     //再创建一个新的头节点,待会儿返回
     ListNode newHead = null;
     while(cur!=null){
        next = cur.next;
        if(next == null){
            //已经全部完成了
            newHead = cur;
            //这个地方不能break,因为下面的操作还需要更新!!!
        }
        cur.next = prev;
        prev = cur;
        cur = next;
     }

思路:首先我们可以计算处链表的长度,其次只要将链表的长度/2 之后再遍历我们就可以得到第二个中间节点的值了

java 复制代码
class Solution {
    public ListNode middleNode(ListNode head) {
        int size = 0;
        for(ListNode cur = head ; cur!= null;cur = cur.next){
            size++;
        }
        int num = size/2;
        ListNode cur = head;
        while(num != 0){
            num--;
            cur = cur.next;
        }
        return cur;
    }
}

思路:1.创建新的链表表示最终结构

2.搞两个引用,分别指向两个链表

3.比较这两个引用的值,谁小,就把哪个节点取出来,插入到新链表的末尾,如果这两个引用,有任何一个指向了null,说明该链表就结束了,就把另一个链表剩余的元素都添加到新链表的末尾即可。

代码:

java 复制代码
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        //1.判断list1为空链表的情况
        if(list1 == null){
            return list2;
        }
        //2。判断list2为空链表的情况
        if(list2 == null){
            return list1;
        }
        //3.考虑一般情况
        //为了使后续的插入方便我们首先创建一个傀儡节点以及末尾节点
        ListNode newHead = new ListNode(0);
        ListNode tail = newHead;
        ListNode cur1 = list1;
        ListNode cur2 = list2;
        while(cur1!=null && cur2 !=null){
            if(cur1.val < cur2.val){
                //cur1比较小 所以把cur1放进来
                ListNode newNode =cur1;
                tail.next = newNode;
                tail = newNode; 
                cur1 = cur1.next;
            }else{
                ListNode newNode =cur2;
                tail.next = newNode;
                tail = newNode; 
                cur2 = cur2.next;
            }
        }
        //出来的时候,要么cur1还有剩余,要么cur2还有剩余
        if(cur1 != null){
            //把剩余的链表给他接上去
            tail.next = cur1;
        }

        if(cur2 != null){
            tail.next = cur2;
        }
        return newHead.next;//不能返回newHead 要返回傀儡节点的下面一个节点
    }
}

写不动了!明天再见!

相关推荐
Wils0nEdwards1 小时前
Leetcode 独一无二的出现次数
算法·leetcode·职场和发展
Y.O.U..2 小时前
力扣HOT100——无重复字符的最长子字符串
数据结构·c++·算法·leetcode
Ludicrouers4 小时前
【Leetcode-Hot100】和为k的子数组
算法·leetcode·职场和发展
Hanson Huang7 小时前
【数据结构】堆排序详细图解
java·数据结构·排序算法·堆排序
Susea&7 小时前
数据结构初阶:队列
c语言·开发语言·数据结构
序属秋秋秋8 小时前
算法基础_数据结构【单链表 + 双链表 + 栈 + 队列 + 单调栈 + 单调队列】
c语言·数据结构·c++·算法
挺6的还10 小时前
4.B-树
数据结构·b树
Tanecious.10 小时前
初阶数据结构--二叉树OJ训练
数据结构
x_feng_x10 小时前
数据结构与算法 - 数据结构与算法进阶
数据结构·python·算法