链表理论基础
①链表是一种通过指针串联在一起的线性结构,由数据域和指针域两部分构成。
②跟数组不一样,数组在内存中是连续存放的,链表在内存中不是连续分布的。
③链表有单链表、双链表和循环链表。
203.移除链表元素
把一些变量的定义名意义记住,根据意义去命名变量。
/**
* 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) {
if(head == null)
return head; //考虑空节点情况
ListNode dummy = new ListNode(-1); //创建数值为-1的头结点
dummy.next = head; //指向第一个节点
ListNode pre = dummy; //当前节点的前驱节点
ListNode cur = head; //当前节点
while(cur != null){ //遍历所有节点
if(cur.val == val)
pre.next = cur.next;
else
pre = cur;
cur = cur.next;
}
return dummy.next; //返回头节点
}
}
707.设计链表
单链表
在插入或者删除操作时,一般都要找到待插入或者待删除节点的前驱节点,然后再进行插入或者删除操作。
设置虚拟头节点的目的是为了在删除或者插入操作时,第一个节点和其他节点可以统一操作,而不需要分类讨论。
这道题目设计链表的五个接口:
- 获取链表第index个节点的数值
- 在链表的最前面插入一个节点
- 在链表的最后面插入一个节点
- 在链表第index个节点前面插入一个节点
- 删除链表的第index个节点
public class ListNode{
int val;
ListNode next;
ListNode(){}
ListNode(int val){
this.val = val;
}
}
class MyLinkedList {
int size; //存储链表中元素个数
ListNode head; //这里的head就是虚拟头节点
public MyLinkedList() {
size = 0;
head = new ListNode(0);
}
public int get(int index) {
if(index < 0 || index >= size)
return -1;
ListNode cur = head;
//因为有虚拟头节点,所以实际上要找第(index+1)个节点
for(int i = 0;i <= index ; i++)
cur = cur.next;
return cur.val;
}
//其实就是在下标为0的节点前插入一个新节点
public void addAtHead(int val) {
addAtIndex(0,val);
}
public void addAtTail(int val) {
addAtIndex(size,val);
}
//找下标为index节点的的前一节点进行插入操作
public void addAtIndex(int index, int val) {
if(index < 0 ||index > size)
return ;
ListNode pre = head;
for(int i = 0; i < index;i++)
pre = pre.next;
ListNode addNode = new ListNode(val); //创建值为val的节点
addNode.next = pre.next;
pre.next = addNode;
size++;
}
public void deleteAtIndex(int index) {
if(index < 0 || index >= size)
return;
ListNode pre = head;
//找到待删除节点的前一节点(设置了虚拟头节点,所以可以统一操作)
for(int i = 0;i < index;i++)
pre = pre.next;
pre.next = pre.next.next;
size--;
}
}
双链表(后面有时间补一下双链表是如何进行这些基础操作的)
206.反转链表
如果再定义一个新的链表,实现链表元素的反转,这是对内存空间的浪费,因此不用定义一个新链表。其实只需要改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表。
首先定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。
然后就要开始反转了,首先要把 cur->next 节点用tmp指针保存一下,也就是保存一下这个节点。
为什么要保存一下这个节点呢,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。
接下来,就是循环走如下代码逻辑了,继续移动pre和cur指针。
最后,cur 指针已经指向了null,循环结束,链表也反转完毕了。 此时我们return pre指针就可以了,pre指针就指向了新的头结点。
①双指针法
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null; //初始化为null
ListNode cur = head; //
ListNode temp; //因为接下来要改变 cur->next 的指向了,将cur->next 指向pre,此时已经反转了第一个节点了.
while(cur != null){
temp = cur.next; //因为cur节点的指针要改变方向了,因此要存放下一节点
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}
②递归法
递归法跟双指针法原理一样,参考下模版就行。