一、力扣题203. 移除链表元素
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
示例 2:
输入:head = [], val = 1 输出:[]
示例 3:
输入:head = [7,7,7,7], val = 7 输出:[]
在做本题前复习了链表的基础知识,发现这道题难度不高,只要对链表的结构认知清晰就能做出来,在此之前没做过链表相关的算法,在语法上对于链表和节点的定义费了点时间。
方法1
删除链表元素分为两种情况,第一种是删除头结点,也就是第一个元素,把头结点的下一个节点定义为新的头结点就可以了;第二种是删除头结点以外的元素,需要将目标结点的上一个节点的next改为指向目标节点的下一个节点,如下图所示:
需要注意的是,当头结点为目标值时,设置下一个结点为新的头结点后,这个新的头结点也可能是目标值,所以需要先遍历,将头结点为目标值的情况全部去除完后,再来遍历删除头结点以外的目标元素。
function removeElements($head, $val) {
while($head != null && $head->val == $val) {
$head = $head->next;
}
$cur = $head;
while($cur->next != null) {
if($cur->next->val == $val) {
$cur->next = $cur->next->next;
} else {
$cur = $cur->next;
}
}
return $head;
}
方法2:虚拟头结点
为了更加方便,可以设置一个虚拟头结点,也就是在原来的头结点前新增一个结点,这样删除链表元素时,就只有一种情况:删除头结点以外的元素。
另外需要注意一点,删除完成后的链表,它的真头结点不一定是原来的头结点,也可能头结点是目标值被删除了,所以不能返回题目原来给我们的$head头结点,而应该返回$dummpHead->next这个头结点。
function removeElements($head, $val) {
$dummyHead = new ListNode();
$dummyHead->next = $head;
$cur = $dummyHead;
while($cur->next != null) {
if($cur->next->val == $val) {
$cur->next = $cur->next->next;
} else {
$cur = $cur->next;
}
}
return $dummyHead->next;
}
二、力扣题707. 设计链表
设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val
和 next
。val
是当前节点的值,next
是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev
以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。
在链表类中实现这些功能:
- get(index):获取链表中第
index
个节点的值。如果索引无效,则返回-1
。- addAtHead(val):在链表的第一个元素之前添加一个值为
val
的节点。插入后,新节点将成为链表的第一个节点。- addAtTail(val):将值为
val
的节点追加到链表的最后一个元素。- addAtIndex(index,val):在链表中的第
index
个节点之前添加值为val
的节点。如果index
等于链表的长度,则该节点将附加到链表的末尾。如果index
大于链表长度,则不会插入节点。如果index
小于0,则在头部插入节点。- deleteAtIndex(index):如果索引
index
有效,则删除链表中的第index
个节点。
示例:
MyLinkedList linkedList = new MyLinkedList(); linkedList.addAtHead(1); linkedList.addAtTail(3); linkedList.addAtIndex(1,2); //链表变为1-> 2-> 3 linkedList.get(1); //返回2 linkedList.deleteAtIndex(1); //现在链表是1-> 3 linkedList.get(1); //返回3
这道题我没有事先去看讲解,自己做出来辽!(叉腰一下)
这道题难度不高,主要考察链表的增删查操作,但是有点繁琐,我写的时候在删除元素的方法里就忘记把链表个数减一了,num和dummpHead作为全局变量本身是要着重注意的。
class MyLinkedList {
/**
*/
private $dummyHead;
private $num;
function __construct() {
$this->num = 0;
$this->dummyHead = new ListNode();
}
/**
* @param Integer $index
* @return Integer
*/
function get($index) {
if($index < 0 || $index >= $this->num) {
return -1;
}
$cur = $this->dummyHead;
for($i = 0; $i <= $index; $i++) {
$cur = $cur->next;
}
return $cur->val;
}
/**
* @param Integer $val
* @return NULL
*/
function addAtHead($val) {
$new = new ListNode($val);
$new->next = $this->dummyHead->next;
$this->dummyHead->next = $new;
$this->num++;
}
/**
* @param Integer $val
* @return NULL
*/
function addAtTail($val) {
$new = new ListNode($val);
$last = $this->dummyHead;
while($last->next != null) {
$last = $last->next;
}
$last->next = $new;
$new->next = null;
$this->num++;
}
/**
* @param Integer $index
* @param Integer $val
* @return NULL
*/
function addAtIndex($index, $val) {
if($index <= 0) {
$this->addAtHead($val);
return;
}
if($index == $this->num) {
$this->addAtTail($val);
return;
}
if($index > $this->num) {
return;
}
$new = new ListNode($val);
$i = 0;
$prev = $this->dummyHead;
while($i != $index) {
$prev = $prev->next;
$i++;
}
$new->next = $prev->next;
$prev->next = $new;
$this->num++;
}
/**
* @param Integer $index
* @return NULL
*/
function deleteAtIndex($index) {
if($index >= 0 && $index < $this->num) {
$i = 0;
$prev = $this->dummyHead;
while($i != $index) {
$prev = $prev->next;
$i++;
}
$prev->next = $prev->next->next;
$this->num--;
}
}
}
/**
* Your MyLinkedList object will be instantiated and called as such:
* $obj = MyLinkedList();
* $ret_1 = $obj->get($index);
* $obj->addAtHead($val);
* $obj->addAtTail($val);
* $obj->addAtIndex($index, $val);
* $obj->deleteAtIndex($index);
*/
三、力扣题206. 反转链表
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
示例 2:
输入:head = [1,2] 输出:[2,1]
示例 3:
输入:head = [] 输出:[]
这道题是直接去看的视频讲解,我在看题的时候想的是遍历到最后再一个个反过来,感觉到自己思维的局限性,没有认识和运用到链表本身的特性。
本题的解法有些类似双指针,也是设置两个结点。因为要将链表反转过来,所以只要把链表中的箭头指向都反转一下就好了,然后最后一个结点就变成反转链表的头结点,而原来的头结点的next就变成指向null。
我们从链表的头结点开始反转,所以需要设置两个结点a和b,a放在头结点上,b设为null,然后把 a 指向 b ,这样头结点就指向null了,然后把 b 移到 a 的位置, 而 a 移到头结点原先指向的下一个结点,再次反转使 a -> b
而在a、b 向右移动的过程中,需要有一个中间值才能完成赋值, 就像两个变量互换值也需要中间值。
移动到最后,a 会指向null,b会指向最后一个结点,此时的反转就已经完成了,所以判断反转完成的条件就是 a 为 null。
方法1:
function reverseList($head) {
$cur = $head;
$prev = null;
while($cur != null) {
$temp = $cur->next;
$cur->next = $prev;
$prev = $cur;
$cur = $temp;
}
return $prev;
}
方法2:递归
递归的逻辑和上面的方法类似,只是使用递归会更加简洁。
function reverseList($head) {
$cur = $head;
$prev = null;
while($cur != null) {
$temp = $cur->next;
$cur->next = $prev;
$prev = $cur;
$cur = $temp;
}
return $prev;
return $this->reverse($head, null);
}
function reverse($cur, $prev) {
if($cur == null) {
return $prev;
}
$temp = $cur->next;
$cur->next = $prev;
reverse($temp, $cur);
}