链表基础理论笔记
链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个指针域,最后一个节点的指针域指向null。链表的入口节点称为链表的头节点,head。
链表分为单链表、双链表以及循环链表,双链表每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点,既可向前查询也可向后查询。循环链表首尾相连,可以用来解决约瑟夫环问题。
链表在内存中不是连续分布的,散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理,通过指针域的指针链接在内存中的各个节点。
链表的定义
注意,如果不定义构造函数使用默认构造函数的话,在初始化的时候就不能直接给变量赋值!
链表的操作
删除某节点:只要将前一个节点的next指针指向该节点的后一个节点即可,在C++里最好再手动释放掉该节点,Java、Python有自己的内存回收机制,不用手动释放。
添加节点:先将需要插入位置的节点的指针赋给添加的节点的指针,再将前一个节点的指针指向新添加的节点即可。
与数组的区别
数组在定义的时候,长度就是固定的,如果想要改动数组的长度,就需要重新定义一个新的数组。数组的适用场景为:频繁查询,较少增删。
链表的长度可以是不固定的,并且可以动态增删,适用场景为:数据量不固定,频繁增删,较少查询。
Java版本的链表定义
public class ListNode {
// 节点的值
int val;
// 下一个节点
ListNode next;
// 节点的构造函数(无参)
public ListNode(){
}
// 节点的构造函数(有两个参数)
public ListNode(int val, ListNode next){
this.val = val;
this.next = next;
}
}
LeetCode 203.移除链表元素
题目要求删除链表中值等于val的节点,第一次用Java写链表删除,还不是很熟练,之前一直用C写题,while循环遍历时还在思考循环的条件要怎么来写,如果current是从dummyHead开始遍历的,由于要取值,所以不仅要判断current!=null还要current->next!=null,如果cureent是从head来遍历的则需要保存上一个节点,所以还需要一个变量来存储上一个节点,所以循环的条件就变为了current!=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 removeElements(ListNode head, int val) {
ListNode dummyHead = new ListNode(-1, head);
ListNode pre = dummyHead;
ListNode current = head;
while(current != null){
if(current.val == val){
pre.next = current.next;
}else{
pre = current;
}
current = current.next; // 更新current节点
}
return dummyHead.next; // 真正的head节点
}
}
最后别忘了返回真正的head节点。
LeetCode 707设计链表
思路:代码写的磕磕绊绊,在挂了一次之后发现是增加节点和删除节点的循环条件不对,改了之后就过了。后来看了下讲解,发现自己的代码还有很大的优化空间,原始代码如下:
class LinkNode{
int val;
LinkNode next;
public LinkNode(){}
public LinkNode(int val){
this.val = val;
}
}
class MyLinkedList {
int size; // 存储链表长度
LinkNode head; // 虚拟头节点
public MyLinkedList() {
size = 0 ;
head = new LinkNode(-1);
}
public int get(int index) {
if(index < 0 || index >= size) return -1;
LinkNode current = head;
for(int i=0; i<=index; i++){
current = current.next;
}
return current.val;
}
public void addAtHead(int val) {
LinkNode newNode = new LinkNode(val);
newNode.next = head.next;
head.next = newNode;
size++;
}
public void addAtTail(int val) {
LinkNode current = head;
while(current.next != null){ // 找到尾节点
current = current.next;
}
LinkNode newNode = new LinkNode(val);
current.next = newNode;
size++;
}
public void addAtIndex(int index, int val) {
if(index >= 0 && index <= size){
if(index == 0){
addAtHead(val);
}else if(index == size){
addAtTail(val);
}else{
LinkNode current = head;
LinkNode newNode = new LinkNode(val);
for(int i=0; i<index; i++){
current = current.next;
}
newNode.next = current.next;
current.next = newNode;
size++;
}
}
}
public void deleteAtIndex(int index) {
if(index >= 0 && index < size){
LinkNode current = head;
for(int i=0; i<index; i++){
current = current.next;
}
LinkNode delNode = current.next; // 暂存要删除的节点
current.next = delNode.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);
*/
在看了文章之后,对代码进行优化如下:
class LinkNode{
int val;
LinkNode next;
public LinkNode(){}
public LinkNode(int val){
this.val = val;
}
}
class MyLinkedList {
int size; // 存储链表长度
LinkNode head; // 虚拟头节点
public MyLinkedList() {
size = 0 ;
head = new LinkNode(-1);
}
public int get(int index) {
if(index < 0 || index >= size) return -1;
LinkNode current = head;
for(int i=0; i<=index; i++){
current = current.next;
}
return current.val;
}
public void addAtHead(int val) {
addAtIndex(0, val);
}
public void addAtTail(int val) {
addAtIndex(size, val);
}
public void addAtIndex(int index, int val) {
if(index > size) return;
if(index < 0) index = 0;
LinkNode current = head;
LinkNode newNode = new LinkNode(val);
for(int i=0; i<index; i++){
current = current.next;
}
newNode.next = current.next;
current.next = newNode;
size++;
}
public void deleteAtIndex(int index) {
if(index >= 0 && index < size){
LinkNode current = head;
for(int i=0; i<index; i++){
current = current.next;
}
LinkNode delNode = current.next; // 暂存要删除的节点
current.next = delNode.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);
*/
加了虚拟头节点后,第i个节点变成了第i+1节点,增加节点和删除节点时,要注意循环次数。
LeetCode 206反转链表
刚开始没啥思路,在看了卡尔的视频讲解后明白了该怎么去做,双指针代码如下:
class Solution {
public ListNode reverseList(ListNode head) {
ListNode current = head;
ListNode pre = null;
while(current != null ){
ListNode temp = current.next;
current.next = pre;
pre = current;
current = temp;
}
return pre;
}
}
熟悉双指针解法后,尝试递归解法如下:
lass Solution {
public ListNode reverse(ListNode current, ListNode pre){
if(current == null) return pre;
ListNode temp = current.next;
current.next = pre;
return reverse(temp, current);
}
public ListNode reverseList(ListNode head) {
return reverse(head, null);
}
}