刷题day3 移除链表元素、设计链表、反转链表

1、清楚链表的结构

单链表  每一个结点由两部分组成,一个是数据域,一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思),链表的入口结点称为链表的头结点也就是head

 双链表  单链表中的指针域只能指向节点的下一个节点,双链表,每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点,双链表既可以向前查询,也可以向后查询

循环链表  链表首尾相连,可以用来解决约瑟夫环的问题

2、链表的存储方式

数组在内存中是连续分布的,链表在内存中可不是连续分布的,链表是通过指针域的指针链接在内存中各个节点

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

如下图:

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

3、链表的操作

 删除节点,如图所示:

添加节点,如图所示: 

 数组和链表的性能分析

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

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

 4、链表的定义

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

5、移除链表元素

这道题我的方法主要是添加一个虚拟的头节点指向真正的头节点,在头节点需要删除的情况下也就是头节点的值等于给定值的时候,操作可以方便许多

首先判断头部节点是不是为空,之后定义一个虚拟头节点,在head之前,然后定义两个指针,分别指向虚拟头节点和头节点,然后进行循环判断是否每次头节点的指针移动的时候头节点是不是为空

然后就是具体的判断值,如果头节点值等于给定值,那么就是删除这个头节点,正常的链表删除操作,将pre.next指向afterpre.next,如果不是,那么就是pre指针移动到afterpre的位置上,之后进行afterpre指针的移动,最后循环结束返回新链表的头节点,由于是已经在操作过程中把新链表的头节点已经正常连接上了,而原来的virtual的位置没有变,所以他的正常next就是新链表的头节点,返回即可

代码如下:

public class ListNode{
    int val;
    ListNode next;
    ListNode();
    ListNode(val){ this.val = val;}
    ListNode(int val,ListNode next){this.val = val; this.next = next;}
    }
class solution{
    public ListNode removeElement(ListNode head,int val){
        if(head == null)
        {
            return head;
        }
        
        ListNode virtual = new ListNode(-1,head);
        ListNode pre = virtual;
        ListNode afterpre = head;
        while(afterpre != null)
        {
            if(afterpre.val = val)
            {
                pre.next = afterpre.next;
            }
            else{
                pre = afterpre;
            }
            afterpre = afterpre.next;
        }
        return virtual.next;
}

6、设计链表

这道题首先是要设计很多的方法

实现 MyLinkedList 类:

  • MyLinkedList() 初始化 MyLinkedList 对象。
  • int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
  • void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
  • void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
  • void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
  • void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。

在设计这些链表的时候需要先创建一个基本的链表结构 ,但是这个算法的链表结构在创建的时候不需要给构造函数传两个值,设计一个默认构造函数一个只传一个值的构造函数即可

在初始化MyLinkedList() 时,需要初始化链表长度0,还有初始化头节点,值为0

在获取值的时候需要考虑index的有效性,设置if判断条件index大于等于size无效,index小于0无效

在首尾添加元素直接调用函数addAtIndex(int index, int val)

在添加元素的时候需要初始化一个新节点,然后取一个pre = head,让pre来循环寻找位置,之后就是判断一下边界大于size返回空,小于0就是插入到开头和index=0同一个效果,之后找index之前的位置,利用i < index ,pre=pre.next来控制,然后插入新节点,最后别忘记节点的长度增加四size++

在删除的时候也是同理,先排除不可能的两种情况,小于0和大于等于size的,找到要删除的元素,通过i<=index,pre=pre.next来控制

代码如下:

class MyLinkedList {
    class   ListNode{
        int val;
        ListNode next;
        ListNode(){}
        ListNode(int val){
            this.val = val;
        }
    }

    int size;
    ListNode head;

    public MyLinkedList() {
        int size = 0;
        head =new ListNode(0);
    }
    
    public int get(int index) {
        ListNode pre = head;
        if(index>=size||index<0)
        {
            return -1;
        }
        for(int i=0;i<=index;i++)
        {
            pre = pre.next;
        }
        return pre.val;
    }
    
    public void addAtHead(int val) {
        addAtIndex(0,val);
    }
    
    public void addAtTail(int val) {
        addAtIndex(size,val);
    }
    
    public void addAtIndex(int index, int val) {
        ListNode pre = head;
        ListNode node1 =new ListNode(val);
        if(index>size)
        {
            return;
        }
        if(index<0)
        {
            index=0;
        }

        for(int i=0;i<index;i++)
        {
            pre = pre.next;
        }
        node1.next = pre.next;
        pre.next = node1;
        size++;
    }
    
    public void deleteAtIndex(int index) {
        if(index < 0 || index>=size)
        {
            return;
        }
        
        ListNode pre = head;
        for(int i=0;i<index;i++)
        {
            pre = pre.next;
        }
        pre.next = pre.next.next;
        size--;
    }
}

/**
 * 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);
 */

7、反转链表

这道题主要是考的双指针的移动,同时还要找一个temp空指针指向下一次循环的值,也就是cur.next的值,最主要还是一步一步改链表节点的指向(其实递归也是可以解决的)

主要思路:一个pre节点在cur节点前面,一个temp节点在cur节点后面,然后开始移动,顺序是temp先移动到cur.next,然后打断链表,cur.next指向pre,之后pre指向cur,cur再指向temp,之后进入下一轮循环,一直到cur指向temp指向null

代码如下:

/**
 * 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 reverList(ListNode head)
    {
        ListNode cur = head;
        ListNode pre = null;
        ListNode temp = null;
        while(cur != null)
        {
            temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值