单链表的结构
一种链式存取的数据结构,单链表中的数据是以结点的形式存在,每一个结点是由数据元素和下一个结点的存储的位置组成。
单链表与数组相比的最大差别是:单链表的数据元素存放在内存空间的地址是不连续的,而数组的数据元素存放的地址在内存空间中是连续的,这也是为什么根据索引无法像数组那样直接就能查询到数据元素。
代码定义
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; }
}
链表的增删改查
插入又分为头插尾插中间插
头插法
1.创建一个新的节点对象node
2.将node节点的next指向head
3.将node赋值给head
4.size加一
public void addFirst(T t){
Node node = new Node(t); //节点对象
node.next = this.head;
this.head = node;
this.size++;
}
尾插法
直接让尾节点的next等于新节点
public void addLast(T t){
this.tail.next = new Node(t);
this.size++;
}
中间插入
1.找到要插入的位置的节点preNode
2.要插入的节点node的next节点指向preNode的next节点
3.preNode的next节点指向新创建的node节点
public void add(T t,int index){
if (index <0 || index >size){
throw new IllegalArgumentException("index is error");
}
if (index == 0){
this.addFirst(t);
return;
}
Node preNode = this.head;
//找到要插入节点的前一个节点
for(int i = 0; i < index-1; i++){
preNode = preNode.next;
}
Node node = new Node(t);
//要插入的节点的下一个节点指向preNode节点的下一个节点
node.next = preNode.next;
//preNode的下一个节点指向要插入节点node
preNode.next = node;
this.size++;
}
链表结点的删除
删除一个结点要区分删除的节点,头节点和其他节点,
1.如果是头节点,直接将head等于head.next
2.如果是其他节点,需要将pre节点的next指向,pre.next.next
因为我们发现在删除的时候需要考虑头节点,这里面有一个小技巧,就是在头节点之前添加一个虚拟节点,最后在通过虚拟节点的next获取原来的头节点
public void removeElt(T t){
//构造虚拟头结点,并且下一个结点指向head
Node dummy = new Node(t,this.head);
//声明结点指向虚拟头结点
Node cur = dummy;
//从虚拟头结点的下一个结点开始遍历
while(cur.next != null){
if(cur.next.t.equals(t)){
cur.next = cur.next.next;
this.size--;
}
else cur = cur.next;
}
//去除虚拟头结点
this.head = dummy.next;
}
这个小技巧在做实际的算法题中很实用,帮助我们减少了对于头节点的空判断,使逻辑统一,接下来我们看一些实战例题,继续探索对于链表结构的应用。上面文章参考 原文链接:blog.csdn.net/weixin_3660…
高频的面试题:
一、反转链表
1.对于这道题,个人认为双指针法比较好理解
- 定义两个指针,pre,cur
- cur指向pre
- cur,pre同时向后移动
- 循环上面的步骤,直到pre达到最后的节点
- 返回pre
动图演示:
代码实现:
class Solution {
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
}
还有一种递归解法,但是鉴于我对递归的解题方法还没有系统性的思维,所以暂时不写, 这里有个老汤哥的讲解比较清晰:感兴趣可以去看看
二、合并两个有序链表
和前面合并两个有序数组类似,用双指针法,依次比较指向的节点,小的那个指针向后移动,直到其中一个走到结尾,之后将另一个的链表剩余部分全部添加到后面,也就是将当前节点直接追加到新链表的尾节点
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode prehead = new ListNode(-1);
ListNode prev = prehead;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
prev.next = l1;
l1 = l1.next;
} else {
prev.next = l2;
l2 = l2.next;
}
prev = prev.next;
}
// 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
prev.next = l1 == null ? l2 : l1;
return prehead.next;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/he-bing-liang-ge-you-xu-lian-biao-by-leetcode-solu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。