单向链表增删查

单向链表

什么是单向链表?

单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。

实现单向链表的构造

单向链表就是如下图一样,类似于铁链一样,元素之间互相连接,有多个节点,每一个节点都有一个指向后边元素的next指针。单向链表的最后一个元素必须指向null。
在这里插入图片描述
现在看一下怎么构造单向链表,首先要理解的是JVM是如何创建出链表的,JVM中有堆区和栈区,栈区主要是存储引用,也就是指向实际对象地址(可以理解为链表的表头),而堆区存储的才是创建的对象。
个人理解:链表的数据主要存储在堆区,栈区存的链表表头只是敲门砖,要想访问链表的数据,需要通过栈区的链表表头,访问堆区的链表。且只能使用next遍历。

例:
首先定义这样一个类

public class Course{
	Teacher teacher;
	Student student;
}

这里的teacher与student就是存在栈中,指向堆的引用。然后如下定义

public class Course{
	int val;
	Course next;
}

这个时候next就指向了下一个同为Course类型的对象了。如图:
在这里插入图片描述
这里就是通过栈中的引用可以找到val(1),然后val(1)节点又存了指向val(2)地址,而val(3)又存了指向val(4)的底子好,所以就构成了一个链条访问结构。

在Java中debug,看单向链表的数据格式是这样的
在这里插入图片描述
链表从head开始访问,然后逐个next向后访问,每次所访问对象的类型都是一样的。

单向链表构造的两种方式
1、根据面向对象的理论,Java中规范的链表应该是这样定义:

public class Node {
    public int var;

    public Node next;

    public Node(int var) {
        this.var = var;
    }

    public int getVar() {
        return var;
    }

    public void setVar(int var) {
        this.var = var;
    }

    public Node getNext() {
        return next;
    }

    public void setNext(Node next) {
        this.next = next;
    }

}

2、但是在LeetCode的算法题中经常使用这样的方式来创建链表:

public class ListNode{
	public int val;
	public ListNode next;
	ListNode(int x){
		val = x;
		next = null;
	}
}
//创建链表
ListNode listNode = new ListNode(1);

这里的val就是当前节点的值,next用来指向下一个节点。因为两个变量都是public的,创建对象后能直接使用listNode.val和listNode.next来操作,虽然违背面向对象的设计要求,但是更精简,所以在算法题目中更广泛。

遍历单向链表的数据

一定要注意的是,对于单向链表,不管执行什么操作,都要从头开始逐个向后访问!!!

遍历链表有多少个节点,代码如下

public static int getListLength(Node head) {
	int length = 0;
	Node node = head; //要在栈里用一个新的地址指向堆中的数据,防止首节点丢失。 
	while(node != null){
		length++;
		//int val = node.next;//表示当前节点数据
		node = node.next;//指向下一个节点后且重新给node对象赋值,以此类推可以一个个访问节点内容。
	}
	return length;
}

插入单向链表的数据

首部插入

注意:head一定要重新指向表头。
现在有一个新节点newNode,然后要连接上现在的表头,那么就用newNode.next = head,这样就是直接连接上表头了,现在newNode就是表头。然后需要把head重新指向表头,那就是head = newNode;
在这里插入图片描述

中间插入

在这里插入图片描述

尾部插入

在这里插入图片描述

首部、中间、尾部插入的代码逻辑实例:


     /**
     * 链表插入
     *
     * @param head       链表头节点
     * @param nodeInsert 待插入节点
     * @param position   待插入位置,从1开始
     * @return
     */
    public static Node insertNode(Node head, Node newNode, int position) {
        if (head == null) {
            throw new RuntimeException();
        }
        int size = getLength(head);//查看链表共有多少个子节点(此方法是上边的遍历节点数据)
        if (position > size + 1 || position < 1) { //判断要插入的节点要小于等于链表总长度加一,因为不能隔一个插入 || 判断要插入的节点必须大于等于1
            System.out.println("越界");
            return head;
        }

        //首部插入
        if (position == 1) {//链表计数从1开始
            newNode.next = head;
            head = newNode;
            return head;
        }
        //-----------------------------------
        //中间和尾部插入
        Node pHead = head;//存留首节点,此时head和pHead同时指向堆区中的同一份数据
        int count = 1;
        while (count < position - 1) {//中间插入时,须找到插入节点的前一位
            pHead = pHead.next;
            count++;
        }
        //例:head中有两个节点1、2,position是2,newNode是3
        //此时pHead的值是1
        newNode.next = pHead.next;//插入到position节点之前(pHead是position的前一位,pHead.next就是position节点)
        //上行运行完毕后,newNode的值是3、2
        pHead.next = newNode;//此时,pHead.next就相当于是1.next,1的next重新指向一组新的数据也就是newNode
        return head;//由于pHead与head指向的是同一份数据,且不是首部插入,所以直接返回head,不用head=pHead
    }

删除单向链表的数据

首部、中间、尾部删除的代码逻辑实例:

    /**
     * 删除节点
     *
     * @param head     链表头节点
     * @param position 删除节点位置,取值从1开始
     * @return 删除后的链表头节点
     */
    public Node deleteNode(Node head, int position) {
        if (head == null) {
            throw new RuntimeException();
        }
        int size = getLength(head);
        if (position < 1 || position > size) {
            System.out.println("参数错误");
            return head;
        }
        if (position == 1) {
            head = head.next;
            return head;
        } else {
            Node pHead = head;
            int count = 1;
            while (count < position - 1) {
                pHead = pHead.next;
                count++;
            }
            Node newNode = pHead.next;//此时newNode节点的数据就是代表着要删除的数据
            pHead.next = newNode.next;//执行此语句时,直接把newNode节点也就是要删除的数据给隔过去,也就是删除了
            return head;
        }
    }

部分内容借鉴 编程导航 知识星球 鱼骨头讲师 文档。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值