单向链表的删除
问题描述
给定一个单链表,删除该链表中某些节点。一个节点该不该删除由函数Remove()决定,该单链表删除的函数原型为 struct ListNode* RemoveIF(struct ListNode* head, RemoveFn* Remove)。下面给出三种解法,主要是想突出第三种使用二级指针的方法。
方法一
这个是我自己瞎憋出来的,跟第二种教科书上的方法非常类似,只是我少用了一个变量,但是逻辑比第二种复杂。基本思路:
- 如果一个节点不要删除,则移动下一个节点;
- 如果一个节点需要删除,则判断它是不是头节点,如果是则要将头节点指针指到下一个节点,这里通过prev_node是否为空来判断;如果不是,则prev_node不动,current_node移到下一个节点。
struct ListNode* RemoveIf1(struct ListNode* head, RemoveFn* Remove)
{
struct ListNode* prev_node = NULL;
struct ListNode* current_node = head;
while(current_node) {
if(Remove(current_node)) {
if (NULL == prev_node){
head = current_node->next;
} else {
prev_node->next = current_node->next;
}
free(current_node);
current_node = (NULL == prev_node)?
head : prev_node->next;
} else {
prev_node = current_node;
current_node = current_node->next;
}
}
return head;
}
方法二
多使用了一个next变量,逻辑简单清楚了一些。
struct ListNode* RemoveIf2(struct ListNode* head, RemoveFn* Remove)
{
struct ListNode* prev_node = NULL;
struct ListNode* current_node = head;
struct ListNode* next_node;
while (current_node) {
next_node = current_node->next;
if (Remove(current_node)) {
if (NULL == prev_node){
head = current_node->next;
} else {
prev_node->next = current_node->next;
}
free(current_node);
} else {
prev_node = current_node;
}
current_node = next_node;
}
return head;
}
方法三
这个方法的优点在于不要判断被删除的节点是不是头节点,逻辑更加清楚。基本原理其实就是只要遇到第一个不要删除的节点之后就不要处理头节点问题了,因为此时头节点已经确定下来。所以在else块里面会移动二级指针,因为此时的head已经确定了。而在if块里面,只是改变了ptr_current_node所指向的内容,如果头节点还没确定时,ptr_current_node与head相同,也就是改变了head指向的头节点位置。这个方法确实效率更高,真是linus讲的core low-level coding。
void RemoveIf3(struct ListNode** head, RemoveFn * Remove)
{
struct ListNode** ptr_current_node = head;
struct ListNode* entry;
while(*ptr_current_node) {
entry = *ptr_current_node;
if (Remove(entry)) {
*ptr_current_node = entry->next;
free(entry);
} else {
ptr_current_node = &entry->next;
}
}
}
测试
#include<stdio.h>
#include<stdlib.h>
struct ListNode
{
int val;
unsigned should_remove:1;
struct ListNode* next;
};
struct ListNode* CreateList(int n)
{
//创建一个list,有n个node
//并设置奇数位置的要删除
struct ListNode* node;
struct ListNode* next_node = NULL;
int i;
for (i = n-1; i >= 0; --i) {
node = (struct ListNode*)malloc(sizeof(struct ListNode));
node->val = i;
node->next = next_node;
node->should_remove = i % 2;
next_node = node;
}
return node;
}
void RemoveList(struct ListNode* head){
struct ListNode* next;
while(head) {
next = head->next;
free(head);
head = next;
}
}
int IsRemove(struct ListNode* node)
{
return node->should_remove;
}
typedef int RemoveFn(struct ListNode*);
void PrintList(struct ListNode* head)
{
while(head->next) {
printf("%d, ", head->val);
head = head->next;
}
if(head) printf("%d
", head->val);
}
int main()
{
printf("test 1: ");
struct ListNode* head1 = CreateList(6);
RemoveIf1(head1,IsRemove);
PrintList(head1);
RemoveList(head1);
printf("test 2: ");
struct ListNode* head2 = CreateList(8);
RemoveIf2(head2,IsRemove);
PrintList(head2);
RemoveList(head2);
printf("test 3: ");
struct ListNode* head3 = CreateList(10);
struct ListNode** p = &head3;
RemoveIf3(p, IsRemove);
PrintList(*p);
return 0;
}
输出
$ ./a.out
test 1: 0, 2, 4
test 2: 0, 2, 4, 6
test 3: 0, 2, 4, 6, 8