关键内容:链表基础;链表常见操作,虚拟头节点

常见链表大致分为:单链表双链表循环链表
关于链表的创建(可以用多个构造函数来初始化)

struct ListNode {
    int val;  // 节点上存储的元素
    ListNode *next;  // 指向下一个节点的指针
    ListNode(int x) : val(x), next(NULL) {}  // 节点的构造函数
    ListNode() :val(0),next(NULL){}
    ListNode(ListNode* p): val(0),next(p) {}
};

链表最基本的操作是删除节点和添加节点

数组与链表的性能比较

206.反转链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head==NULL)               //开始时忘记考虑空链表!!!
        {
            return NULL;
        }
        if(head->next==NULL)
        {
            return head;
        }
        ListNode* pre=head;
        ListNode* tmp=head->next;
        ListNode* aft=tmp->next;
        if(head->next->next==NULL)
        {
            tmp->next=head;
            head->next=NULL;
            return tmp;
        }
        while(tmp!=NULL)
        {
            tmp->next=pre;
            pre=tmp;
            tmp=aft;
            if(aft!=NULL)      //开始忘记考虑最后一步aft为NULL,导致了内存错误(NULL没有next)
            aft=aft->next;     //这警示着笔者,对待边界等特殊的点要考虑清楚,周全
        }
        head->next=NULL;
        return pre;
    }
};

203.移除元素

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode* dp=new ListNode(0,head);  //此处为设置虚拟头节点
        ListNode* cur=new ListNode;
        cur=dp;     //创建dp,不是为了移动,dp可以理解为固定在那里,为了方便头节点操作一致性
        ListNode* tmp=head;  //而cur的创建才是为了移动,tmp也是,笔者开始将dp在那里动,错误。
        while(tmp!=NULL)
        {
            if(tmp->val==val)
            {
                cur->next=tmp->next;
                tmp->next=NULL;
                tmp=cur->next;
                continue;
            }
            cur=tmp;
            tmp=tmp->next;
        }
        head=dp->next;       //此一行代码极度容易忽略!!!
        delete dp;
        return head;
    }
};

总结
操作链表时,可用原链表直接操作,也可设置虚拟头节点再操作,主要是为了方便头节点操作的一致性;
要清楚在那移动的是什么指针,相对不动的是什么(虚拟头显然是后者);
指针处理要当心,防止NULL->next之类的隐含错误;
一般链表构造时建议使用构造函数为next赋NULL,若不如此,日后用new创建时,对next的操作若遗忘则会出错(但实际上直接写point->next=是可以的,只是不能改变指向的具体值)。