LeetCode203.移除链表元素
给你一个链表的头节点
head
和一个整数val
,请你删除链表中所有满足Node.val == val
的节点,并返回 新的头节点 。
思路:题目看来很简单,在链表中找到符合条件的节点进行删除即可。
有两种方式:
1、直接在原链表上进行删除,此时需要考虑头节点为需要删除的元素所在节点的情况,因此需要分情况讨论,即元素在头节点和不在头节点,分情况讨论即可,相对来说比较容易,这里不过多讨论。
2、采用虚拟节点的思想,设置一个虚拟节点,将其插入原链表的开头位置,这样不管需要删除的元素在头节点或者不在头节点,都按照不在头节点情况进行讨论,减少了分情况讨论的代价,但是需要注意的是最后返回的是虚拟节点后面的next域,而非新增加的虚拟节点。
需要注意的是,不管采用哪种方法,判断的条件比如p != NULL或者p->next != NULL要写对,多琢磨琢磨,另外对于删除的元素要及时将其delete掉,以免成为乱指的野指针,造成各种各样问题。
ListNode* removeElements(ListNode* head, int val) {
ListNode* virtualNode = new ListNode(0, head); //创建一个虚拟节点指向原链表
ListNode* p = virtualNode;
while(p != NULL && p -> next != NULL){
if(p -> next -> val == val){
ListNode* tmp = p -> next;
p ->next = p -> next -> next;
delete tmp;
}else{
p = p -> next;
}
}
head = virtualNode -> next;//将头节点指向虚拟节点的next
delete virtualNode; //及时删除虚拟节点,节省空间
return head;
}
LeetCode707.设计链表
你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:
val
和next
。val
是当前节点的值,next
是指向下一个节点的指针/引用。如果是双向链表,则还需要属性
prev
以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。实现
MyLinkedList
类:
MyLinkedList()
初始化MyLinkedList
对象。int get(int index)
获取链表中下标为index
的节点的值。如果下标无效,则返回-1
。void addAtHead(int val)
将一个值为val
的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。void addAtTail(int val)
将一个值为val
的节点追加到链表中作为链表的最后一个元素。void addAtIndex(int index, int val)
将一个值为val
的节点插入到链表中下标为index
的节点之前。如果index
等于链表的长度,那么该节点会被追加到链表的末尾。如果index
比长度更大,该节点将 不会插入 到链表中。void deleteAtIndex(int index)
如果下标有效,则删除链表中下标为index
的节点。
思路:题目虽然看起来多,但是其实并不复杂,当找到头节点后,一切按照题目描述进行即可。这里依然采用虚拟指针的方法。
struct LinkNode{
int val;
LinkNode* next;
LinkNode(){};
LinkNode(int value): val(value){}
LinkNode(int value, LinkNode* n): val(value), next(n){}
};
private:
LinkNode* dummyNode;
int size = 0;
public:
MyLinkedList() {
dummyNode = new LinkNode(0, NULL); //采用虚拟节点进行初始化
}
int get(int index) {
if(index > (size - 1) || index < 0) return -1;
LinkNode* p = dummyNode -> next;
while(index --){ //注意这里不能写为--index,否则会进入死循环
p = p -> next;
}
return p -> val;
}
void addAtHead(int val) {
LinkNode* newNode = new LinkNode(val);
newNode -> next = dummyNode -> next;
dummyNode -> next = newNode;
size ++;
}
void addAtTail(int val) {
LinkNode* newNode = new LinkNode(val, NULL);
LinkNode* p = dummyNode;
while(p != NULL && p -> next != NULL){
p = p -> next;
}
p -> next = newNode;
size ++;
}
void addAtIndex(int index, int val) {
if(index > size) return;
LinkNode* newNode = new LinkNode(val, NULL);
LinkNode* p = dummyNode;
while(index --){
p = p -> next;
}
newNode -> next = p -> next;
p -> next = newNode;
size ++;
}
void deleteAtIndex(int index) {
if(index > size - 1 || index < 0) return;
LinkNode* p = dummyNode;
while(index--){
p = p -> next;
}
LinkNode* tmp = p -> next;
p -> next = p -> next -> next;
delete tmp;
tmp = nullptr; //需要将删除的节点赋空值,否则会成为野指针,出现各种各样问题
size --;
}
LeetCode206.反转链表
给你单链表的头节点
head
,请你反转链表,并返回反转后的链表
思路:反转链表的题目还是比较经典,主要是采用双指针来进行,其实可以成为三指针法,因为过程中还用到了tmp指针来保存cur的next节点。
主要有两种解题顺序:
1、从前往后反转,常用方法有递归法和双指针法;
2、从后往前反转,常用方法有递归法;
当然还有其他方法,比如创建虚拟节点,将原链表的结点依次链接到虚拟结点上,每次增加都是作为头节点增加,最后发挥虚拟结点的next即可;还有一种方法是使用栈,新建一个链表,将原链表元素依原顺序入栈,而后再弹出,将弹出的元素依次添加到新的链表中,每次添加都是作为末尾结点添加,这样就能得到反转后的链表,当然这时候还要注意原链表的第一个结点的next需要指向NULL,因为本身它的next是指向原链表第二个元素的,弹出后仍然指向原链表的第二个元素,所以需要修改指向。
双指针法(从前往后反转)
ListNode* reverseList(ListNode* head) {
ListNode* cur = head;
ListNode* pre = NULL;
ListNode* tmp;
while(cur != NULL){
tmp = cur -> next; //保存后面的节点
cur -> next = pre; //指向前面的节点
pre = cur;
cur = tmp; //注意交换的顺序很关键
}
return pre; //此时为最后一个节点的指针,即翻转生成的链表的头节点指针
}
递归法(从前往后反转)
ListNode* reverse(ListNode* pre, ListNode* cur){
if(cur == NULL) return pre;
ListNode* tmp = cur -> next;
cur -> next = pre;
return reverse(cur, tmp); //思路与双指针类似,这里的递归调用相当于pre=cur;cur=tmp;
}
ListNode* reverseList(ListNode* head) {
return reverse(NULL, head);
}
递归法(从后往前反转)
ListNode* reverseList(ListNode* head) {
//临界条件判断
if(head == NULL) return NULL;
if(head -> next == NULL) return head;
//对非头结点及之后的结点进行反转
ListNode* last = reverseList(head -> next);
//对第一个和第二个节点进行反转
head -> next -> next = head;
head -> next = NULL;
return last;
}
希望能够帮助到你。