链表相较于数组,它的内存是不连续的,而是散乱分布在内存中的某地址上。
数组的元素删除需要进行覆盖操作,而链表的增删都是O(1)操作,不会影响到其他节点。
可以看出,链表的数据量是不固定的,其特性使其适用于频繁增删的情况下;而数组数据量固定,特性是更适合用于较多查询的情况下。
虚拟头节点可以帮助统一对链表的操作方式。当元素位于首元节点、中间位置或是最后一个元素时,在不使用虚拟头节点的情况下,需要按情况进行分类讨论,有可能会让代码变得更复杂难懂。
虚拟头节点就是:在首元节点之前加一个虚拟头节点,使虚拟头节点指向首元节点。
203. 移除链表元素
这里定义了一个指针变量cur,作为当前遍历的节点,用于帮助从首元结点开始遍历链表。
考虑一下如何对元素进行删除:因为链表的每个节点包含了数据域和指针域,指针域的指针指向了节点的下一个元素。所以只要使得被删除节点的前一个节点的指针域指向被删除节点的后一个节点就可以了。(相当于跨过了被删除节点)
因为单向链表是不能够得到前驱信息的,所以在遍历时,应当使得cur->next
指向要被删除的元素,这样一来才能得到被删除元素的上一个节点的信息(也就是cur)
class Solution { public: ListNode* removeElements(ListNode* head, int val) { //设置虚拟头节点 ListNode* dummyNode = new ListNode(0); dummyNode->next=head; ListNode* cur=dummyNode; while(cur->next!=NULL){ if(cur->next->val==val){ //确认下一个节点是要被删除的元素,更改next的指向 cur->next=cur->next->next; } else cur=cur->next; } return dummyNode->next; } }; //new ListNode(0)是用于动态分配内存并初始化一个新的ListNode对象的语法。在这种情况下,ListNode(0)表示调用ListNode的构造函数,传递参数0来创建一个新的ListNode对象。
C++没有内存自动释放机制,不会自动将节点的内容从内存中释放,需要用delete手动清理内存
//释放掉冗余内存减少开销 class Solution { public: ListNode* removeElements(ListNode* head, int val) { //设置虚拟头节点 ListNode* dummyNode = new ListNode(0); dummyNode->next=head; ListNode* cur=dummyNode; while(cur->next!=NULL){ if(cur->next->val==val){ //delete用于释放空间 ListNode* tmp=cur->next; cur->next=cur->next->next; delete tmp; } else cur=cur->next; } head=dummyNode->next; delete dummyNode; return head; } };
(其余两题晚上更新)