链表基础及练习题
目录
1.链表
1.1 什么是链表?
链表(LinkdeList)是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。非线性的顺序存储。
1.2 链表的类型
单链表:分为两部分,Value存储数据,Next存放指向下一节点的指针。一个单链表的第一个节点称作头节点,最后一个节点指向Null;
双链表: 分为三个部分,Prev存储指向上一节点的指针,Value存储数据,Next存放指向下一节点的指针。头节点的Prev指向Null,尾节点的Next指向Null。
两种方式相比:单链表只能单方向查找,删除的时候需要引入临时节点。
1.3 两种链表节点的定义
单链表节点的定义:
class ListNode{
//数据域Value
int value;
//指针域,存放下一个节点
ListNode next;
public ListNode() {}
public ListNode(int value, ListNode next) {
this.value = value;
this.next = next;
}
}
双链表节点的定义:
class ListNode {
int value;
ListNode prev;
ListNode next;
public LinkedNode() {}
pubilc LinkedNode(int value,ListNode prev,ListNode next) {
this.value = value;
this.prev = prev;
this.next = next;
}
}
1.4 链表和数组区别
查改:频繁使用查找时,更多使用数组。因为链表查找元素需要遍历链表。
增删:频繁增删时,更多使用链表。数组长度不可变,每次增删需要创建新数组。
1.5 链表的方法
2.相关例题
2.1 LeetCode203.移除链表元素
题目描述:给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
输入:head = [], val = 1 输出:[]
输入:head = [7,7,7,7], val = 7 输出:[]
思路:如果链表为空,直接返回Null。如果链表头head.val == val,则head = head.next,直到不同的头节点出现。head存储头节点,所以需要一个临时节点cur来操作。
class Solution {
public ListNode removeElements(ListNode head, int val) {
while(head!=null && head.val==val){
head = head.next;
}
ListNode curr = head;
while(curr!=null){
while(curr.next!=null && curr.next.val == val){
curr.next = curr.next.next;
}
curr = curr.next;
}
return head;
}
}
需要注意:[1,2,3,3,4,5],val = 3。第一次写的时候,直接用if判断,发现有多个连续的节点的值和val相等,则只会删除第一个。所以需要用while判断,直到出现值和val不同的节点。
while(curr.next!=null && curr.next.val == val){
curr.next = curr.next.next;
}
虚拟头节点法:普通方法中,开始时需要对头节点单独进行操作,所以添加一个虚拟头节点,把头节点也当成普通节点处理。
class Solution {
public ListNode removeElements(ListNode head, int val) {
if (head == null) {
return null;
}
ListNode fakeHead = new ListNode(-1,head);
ListNode curr = fakeHead;
while(curr!=null){
while(curr.next!=null && curr.next.val == val){
curr.next = curr.next.next;
}
curr = curr.next;
}
return fakeHead.next;
}
}
注意:返回值是fakeHead.next而不是直接返回head。fakeHead.next才是新的头节点,
cur临时节点:和当前节点指向同一块内存。如果cur.next = val,则cur = cur.next.next。即cur和cur.next之间的联系断开,但是cur.next和cur.next.next仍存在联系。这也是为什么返回值不能是head的原因。如[7,3,2,1],Val = 7。head.val = val。但是head.next仍然指向3。
2.2 LeetCode206.反转链表
题目描述:给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
/**
* 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 reverseList(ListNode head) {
ListNode cur = head;
ListNode prev = null;
ListNode temp = null;
while (cur != null) {
//1.将临时节点temp = cur.next。因为步骤2中,cur.next将要指向前一节点。防止
和第二个节点的指针丢失,要进行保存。
temp = cur.next;
//2.将cur的下一指针指向前一节点。
cur.next = prev;
//3.更新指针
prev = cur;
cur = temp;
//temp = null;让temp初始化
}
return prev;
}
}
2.3 LeetCode707.设计链表
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) {
this.val = val;
}
}
class MyLinkedList {
int size = 0;
//虚拟头节点,并不是真正的头节点,链表开始是空的,真正的头节点是第一次加入的节点。
ListNode fakeHead;
public MyLinkedList() {
size = 0;
fakeHead = new ListNode(0);
}
public int get(int index) {
if(index < 0 || index >= size) {
return -1;
}
ListNode cur = fakeHead;
while (index >= 0){
cur = cur.next;
index--;
}
return cur.val;
}
public void addAtHead(int val) {
ListNode newNode = new ListNode(val);
newNode.next = fakeHead.next;
fakeHead.next = newNode;
size++;
}
public void addAtTail(int val) {
ListNode newNode = new ListNode(val);
ListNode curNode = fakeHead;
while(curNode.next != null){
curNode = curNode.next;
}
curNode.next = newNode;
size ++;
}
public void addAtIndex(int index, int val) {
if (index > size) {
return;
}
if(index < 0) {
index = 0;
}
size++;
ListNode newNode = new ListNode(val);
ListNode curNode = fakeHead;
for (int i = 0; i < index; i++) {
curNode = curNode.next;
}
newNode.next = curNode.next;
curNode.next = newNode;
}
public void deleteAtIndex(int index) {
if(index < 0 || index >= size) {
return;
}
size --;
ListNode curNode = fakeHead;
for (int i = 0; i < index; i++) {
curNode = curNode.next;
}
curNode.next = curNode.next.next;
}
}
/**
* 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);
*/