链表的基本操作

一、链表的定义

链表由一个个节点连接而成的,即一个个对象,该对象包含两个属性:节点值、下一个节点的引用。同时也可以看到value属性是用来保存实际信息的,而next属性是用来建立节点间的联系。对于双向链表,需要两个这样的属性来指向前面节点和后面节点。

public class ListNode {
    int value; //节点值
    ListNode next = null; //节点的下一个指针
    
    ListNode(int value) {
        this.value = value;
    }
}

二、单向链表基本的操作

1、遍历

​ *链表是一个带头大哥,拉着一群小弟。*所以先找到带头大哥,然后顺藤摸瓜往后揪出所有小弟。小弟的数量也是有限的,最后一个小弟的next属性为null时,表示不再指向其它小弟了,即链表结束了。

 public static void printList(ListNode pNode) {
        if (pNode == null) {
            System.out.println("链表不存在");
        }
        while (pNode != null) { //当前节点不为空
            System.out.print(pNode.value + " ");
            pNode = pNode.next;
        }
 }

​ 可以类比数组的遍历操作去理解,arr.length == 0pNode == null 都是表示数组或链表中没有元素这种情况。数组中遍历元素的方式:

int i = 0;
while (i != arr.length) {
    System.out.println(arr[i]);
    i ++;
}

链表中遍历元素的方式:

while (pNode != null) {
    System.out.println(pNode.value);
    pNode = pNode.next;
}

对比发现,数组和链表都具有流动性,即从头到尾的索引变化。数组受长度限制,而链表受当前节点是否存在限制。

2、删除链表中的某个节点

​ 在数组中删除的思想是覆盖,先找到被删除元素的位置,然后将其后的元素向前移动一位。而链表中就不是覆盖了,思路是,先找到要删除节点的前一个节点preNode ,然后由当前节点curNode找到它的下个节点afterNode = curNode.next,再将preNode.next = afterNode,最后释放curNode节点的空间。这是由链表这种数据结构决定的,只用改变链表的指向,再释放被删节点的空间。

 public static void deleteNode(ListNode pHead, ListNode pToBeDeleted) {

        ListNode pre = pHead;              // 前一个节点
        ListNode curNode = pHead;          //当前节点

        //找到要删的节点
        while (curNode != pToBeDeleted) {
            pre = curNode;                 //记录被删节点的前节点
            curNode = curNode.next;
        }
        ListNode aimNode = curNode.next;   //目标节点
        pre.next = aimNode;                //前节点指向目标节点
 }

​ 在链表的操作中,要明白curNode = curNode.next;跟数组中i ++的目的是一样的。这种方法需要先找到被删节点的前一个节点,这需要0(n)的时间复杂度。有没有0(1)的解法内,我们想想链表中有效的东西是value值不同,next只起到串联的作用。

​ 我们可以这样做,既然入参传入被删除的节点,可拿到它后一个节点 pNext=pToBeDeleted.next将有一个节点value值赋给要删除节点pToBeDeleted.value =pNext.value,这样就不用遍历删除pNext即可。

public static void deleteNode1(ListNode pHead, ListNode pToBeDeleted) {

        if (pHead == null || pToBeDeleted == null)
            return;
        //如果链表只有一个节点且是删除节点
        if (pHead == pToBeDeleted && pHead.next == null) {
            pHead =null;
        }
        ListNode pNext = pToBeDeleted.next;
        ListNode pNextNext = pNext.next;
        pToBeDeleted.value = pNext.value;

        pToBeDeleted.next = pNextNext; //要删节点指向下下个节点
}

这有点像数组中覆盖删除元素。

3、实际问题

链表中最多的是指针操作,搞清每个指针的定义,维持他们之间的不变关系。

典型题目分析

1、链表反转

题目链接

三个指针解法


public ListNode reverseList(ListNode head) {
  	ListNode pre = null;
  	ListNode next = null;
  	ListNode cur = head;
  	while (cur != null) {
      	// 当前结点的后一结点需要保存下来
    	next = cur.next;
      	// 反转
    	cur.next = pre;
      	// 指针前移
    	pre = cur;
    	cur = next;
  	}
  	// 最后pre到达的位置刚好是最末尾那个结点,即反转后的头结点
  	return pre;
}

解题思想


设置三个指针,分别指向当前一个节点、当前节点、后一个节点。当前节点的next本来是指向它的后一个节点的。现在让它指向它前一个节点,就实现了链表的反转。但是当前节点与它后一个节点连接就断开了,因此在反转链表之前需要保存当前节点的下个节点。以便链表反向的过程中向前推进(当前指针和前一指针前移)。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值