先手敲一遍ListNode
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. 移除链表元素
这一题思路还是蛮简单的,我拿到这题的想法是:
一个指针(虚拟节点)直接指向当前节点,若它的下一个节点是要删的节点,则当前节点直接指向下下个节点,否则指针向后移动。
具体做法是:
1. 先考虑下head是不是null
是null,直接返回null
不是null,它的值是不是val,是val就将下一个节点当head,直到找到的head的值不是val
2. curr指针指向head,开始向后移动
3. 看curr和curr.next是否为null,是则结束
4. 看curr下一个节点的值是不是val
是,当前节点直接指向下下个节点
否,curr移动到下一个节点
循环该步
5. 返回head
class Solution {
public ListNode removeElements(ListNode head, int val) {
// if (head == null) {
// return head;
// }
while(head != null && head.val == val) {
head = head.next;
}
ListNode currNode = head;
while(currNode != null && currNode.next != null){
if(currNode.next.val == val){
currNode.next = currNode.next.next;
}else{
currNode = currNode.next;
}
}
return head;
}
}
看了卡哥的讲解后学到了第二种解法,虚拟头节点
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummyHead = new ListNode(0,head);
ListNode currNode = dummyHead;
while(currNode != null && currNode.next != null){
if(currNode.next.val == val){
currNode.next = currNode.next.next;
}else{
currNode = currNode.next;
}
}
return dummyHead.next;
}
}
这种写法个人感觉有两个要注意的点
1. dummyHead 要是new出来的,不能直接赋值
2. 返回的是dummyHead.next,原head有可能已经被删除
2.设计链表
没什么好说的,一开始题都没看明白,直接看的讲解,然后自己写了一遍
class MyLinkedList {
int size;
ListNode dummyHead;
public MyLinkedList() {
size = 0;
dummyHead = new ListNode(0);
}
public int get(int index) {
if(index < 0 || index > size - 1){
return -1;
}
ListNode curr = dummyHead;
for(int i = 0; i <= index; i++){
curr = curr.next;
}
return curr.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 < 0 || index > size){
return;
}
ListNode curr = dummyHead;
for(int i = 0; i < index; i++){
curr = curr.next;
}
curr.next = new ListNode(val, curr.next);
// ListNode temp = new ListNode(val);
// temp.next = curr.next;
// curr.next = temp;
size++;
}
public void deleteAtIndex(int index) {
if(index < 0 || index >= size){
return;
}
ListNode curr = dummyHead;
for(int i = 0; i < index; i++){
curr = curr.next;
}
curr.next = curr.next.next;
size--;
}
}
不管是增还是删,都是对目标节点的前一个节点进行操作,也就是说curr指针指到 index-1 的位置就行了。
3. 反转链表
这题之前在别的地方看过,也比较简单,琢磨一会也就做出来了
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode curr = head;
ListNode temp = null;
while(curr != null){
temp = curr.next;
curr.next = pre;
pre = curr;
curr = temp;
}
return pre;
}
}
核心思想就是定义双指针
pre初始值为null,可以看成是在head前加了一个空头,curr从head开始
然后在curr指向pre前,把后一个节点存在temp中
接着双指针都向后移动,pre到curr的位置,curr到temp的位置。
最后curr为null时,说明已经走完,而此时pre的位置就是新头了。
看了视频之后,学习到了递归写法(以前没怎么接触过递归,在理解双指针写法的情况下,还是很容易理解的
class Solution {
public ListNode reverseList(ListNode head) {
return reverse(null, head);
}
public ListNode reverse(ListNode pre, ListNode curr){
if(curr == null){
return pre;
}
ListNode temp = curr.next;
curr.next = pre;
return reverse(curr, temp);
}
}
reverse函数的结束条件自然是前面循环结束的条件,curr为null时,返回pre
初始是将null和head反转,相当于将head变为tail
反转的方法还是一样的,要准备一个temp
后面的递归,就是将pre和curr后移后指向的元素的反转,即将curr和temp递归