以下题目均为IO型。
1.给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
 
   题目示例如上:
解题思路:
双指针问题,给定指针prev和cur,从头结点开始往后遍历,分两种情况讨论:
- 假如头节点为要删除的节点 
- 假如中间节点为要删除的节点 
prev记录要删除节点的前一个节点,cur记录要删除的节点
假如是第一种情况,cur->val == val时,
            if(cur == head)//1.头删
            {
                head = cur->next;
                free(cur);
                cur = head;
            }假如是第二种情况,
prev->next = cur->next;
free(cur);
cur = prev->next; 
   完整代码如下:
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
//画图分析才会写
typedef struct ListNode ListNode;
 
struct ListNode* removeElements(struct ListNode* head, int val)
{
    ListNode*cur = head;
    ListNode*prev = head;
    while(cur)
    {
        if(cur->val==val)
        {
            if(cur == head)//1.头删
            {
                head = cur->next;
                free(cur);
                cur = head;
            }
            else//2.中间删除
            {
                prev->next = cur->next;
                free(cur);
                cur = prev->next;
            }
        }
        else
        {
            prev = cur;
            cur = cur->next;
        }
    }
    return head;
}2.给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
 
   反转链表较为简单,下面介绍两种方法:
- 三指针遍历法 
 
   给定三个指针prev为NULL,cur = head,next = cur->next,这样往后遍历即可。
typedef struct ListNode ListNode;
法1,三指针往后迭代,prev,cur,next往后,画图才会写
struct ListNode* reverseList(struct ListNode* head)
{
    ListNode*prev = NULL;
    ListNode*cur = head;
    while(head)
    {
        cur = cur->next;
        head ->next = prev;
        prev = head;
        head = cur;
    }
    return prev;
}
- 头插法 
 
   typedef struct ListNode ListNode;
法2,头插
struct ListNode* reverseList(struct ListNode* head)
{
    ListNode*newhead = NULL;
    ListNode*cur = head;
    while(cur)
    {
        ListNode*next = cur->next; 
        //头插
        cur->next = newhead;
        newhead = cur;
        //迭代往后
        cur = next;
    }
    return newhead;
}3.给定一个头结点为 head 的非空单链表,返回链表的中间点。如果有两个中间结点,则返回第二个中间结点。
 
   只需要使用两个指针,fast和slow指针即可,快指针一次走两步,慢指针一次走一步,这样快指针走完时,慢指针一定走到中间节点。
 
   //最优解:快慢指针,慢指针一次走一步,快指针一次走两步
struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode*slow = head,*fast = head;
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
    }
    return slow;
}4.输入一个链表,输出该链表中倒数第k个结点。
 
   解题思路:仍然使用快慢指针:但是这样的快慢指针不是像上一道题快指针走两步,慢指针走一步。
这里的快慢指针是快指针先走,慢指针后走。
fast先走k步,然后fast和slow同时走。
当fast == NULL时,slow即为倒数第k个
原因:快指针先走的k步,消除了倒数的概念。
typedef struct ListNode ListNode;
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) 
{
    ListNode*fast = pListHead,*slow = pListHead;
    int count =0;
    while(count <k)
    {
        if(fast == NULL)
        {
            return NULL;
        }
        fast = fast ->next;
        count++;
    }
    while(fast)
    {
        slow = slow->next;
        fast = fast->next;
    }
    return slow;
}5.将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
 
   解题思路:
- 使用带哨兵位的头节点。2。不使用带哨兵位的头节点 
- 使用带哨兵位的头节点时,好处在于不需要将最小的节点作为头节点,劣势在于由于哨兵位的头节点是malloc出来的,使用完之后需要释放,在释放之间还需要保存哨兵位的头节点的next。 
 
   哨兵位的头节点的好处是,不需要再把第一个小的值给head
typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
带哨兵位的头解法:
    if(list1==NULL)
        return list2;
    if(list2 == NULL)
        return list1;
    //带哨兵位的头节点
    ListNode*head = NULL;
    head = (ListNode*)malloc(sizeof(ListNode));
    ListNode*tail = head;
    while(list1 && list2)
    {   
        if(list1->val < list2->val)
        {
            tail->next = list1;
            tail = list1;
            list1 = list1->next;
        }
        else
        {
            tail->next = list2;    
            tail = list2;
            list2 = list2->next;
        }
    }
    if(list1)
    {
        tail->next = list1;
    }
    if(list2)
    {
        tail->next = list2;
    }
    //注意这里,不能用prevhead = head,然后return prevhead->next
    //因为prev = head,head已经free了,相当于prev非法访问了。
    //所以只能prev = head->next,return prev;
    ListNode*prevhead = head->next;
    free(head);
    return prevhead;
}6.现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。
 
   举个简单的例子:
 
   上面的链表是原链表,定值x是4,也就是小于4的节点需要在前面,大于4的节点在后面,并且不能改变原来链表的节点的顺序。下面的链表即为题目要求的链表。
注意:
 
   由于原链表中的6的next仍然指向2,会造成出现环形链表的情况,会造成死循环,所以需要将6的next 置为NULL。
思路:将小于定值x和大于定值x的链表分开,分别连接后,最后在将小的链表和大的链表连接起来。
 
   class Partition {
  public:
    ListNode* partition(ListNode* pHead, int x) {
        // write code here
        struct ListNode*LessHead ,*LessTail,*GreaterHead,*GreaterTail;
        LessHead=LessTail=GreaterHead=GreaterTail= NULL;
        LessTail = LessHead= (struct ListNode*)malloc(sizeof(struct ListNode));
        GreaterTail= GreaterHead = (struct ListNode*)malloc(sizeof(struct ListNode));
        struct ListNode* cur = pHead;
        while (cur) 
        {
            if (cur->val < x)
            {
                LessTail->next = cur;
                LessTail = cur;
            } 
            else 
            {
                GreaterTail->next = cur;
                GreaterTail = cur;
            }
            cur = cur ->next;
        }
        //到这里说明cur走完链表了,此时需要将两个链表连接起来
        LessTail->next = GreaterHead->next;
        //注意:一定要将GreaterTail->next = NULL
        //假如链表为2->3->5->7->6->2,GreaterTail->next还指向2
        //造成链表成环形了,所以要置空
        GreaterTail->next = NULL;
        ListNode* newnode = LessHead->next;
        free(LessHead);
        free(GreaterHead);
        return newnode;
    }
};7.
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。
 
   思路:所谓的回文结构,其实就是判断链表是否对称。
- 先找出链表的中间节点,然后以该中间节点为界,将链表分割。 
- 前面的链表称为list1,后面的链表称为list2。 
- 将list2逆置,再将list1和list2的节点逐个判断,如果list1或者list2的节点都相等,那就是回文结构。 
 
   //链表回文的思路:
// 1、先找出链表的中间节点,(偶数个找第二个中间节点)
// 2.中间节点作为新的链表头进行逆置。
// 将新逆置后的链表和原链表的val相比,(因为原链表的某些节点的next存储着新链表的某些节点的地址)
ListNode* MidListNode(ListNode* A)
{
    ListNode*slow =A,*fast = A;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}
ListNode* reverseListNode(ListNode* mid)
{
    ListNode*prev = NULL;
    ListNode*cur = mid;
    while(cur)
    {
        ListNode*pnext = cur->next;
        cur->next = prev;
        prev = cur;
        //迭代往后
        cur = pnext;
    }
    return prev;
}
class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) {
        // write code heretypedef struct ListNode ListNode;
        // write code here
        ListNode*mid = MidListNode(A);
        ListNode*rhead = reverseListNode(mid);
        ListNode*curA = A;
        ListNode*curR = rhead;
        while(curA && curR)
        {
            if(curA->val!=curR->val)
            {
                return false;
            }
            else
            {
                curA = curA->next;
                curR = curR->next;
            }
        }
        return true;
    }
};
8.给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
 
   思路:给定一个fast指针和slow指针,先分别遍历两个链表,记录长度,让fast指针指向长链表,fast指针先走 长链表长度-链表长度,随后slow指针从短链表的头开始,和fast链表一起往后走,每个指针一次走一步,如果相等,则相等的点为相交点。
原因:快指针先走的长度差消除了长短链表的长度差。
 typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    ListNode*curA = headA;
    ListNode*curB = headB;
    
    int i = 0;
    int j = 0;
    //先记录两个链表的长度,随后让长链表先走k步,再一起走,就消除了长度差,时间复杂度为O(N),空间复杂度为O(1);
    //还可以再遍历完两个链表的同时,记录两个链表的尾结点,如果尾结点不相同,那肯定不相交,直接返回NULL
    ListNode*TailA = NULL;
    ListNode*TailB = NULL;
    while(curA)
    {
        ++i;
        TailA = curA;
        curA = curA->next;
    }
    while(curB)
    {
        ++j;
        TailB = curB;
        curB = curB->next;
    }
    if(TailA !=TailB)
    {
        return NULL;
    }
    if(i>j)
    {
        int k = i-j;
        curA = headA;
        curB = headB;
        while(k--)
        {
            curA = curA->next;//长链表先走k步
        }
        while(curB && curA)
        {
            if(curA == curB)
            {
                return curA;
            }
            else
            {
                curA = curA->next;
                curB = curB->next;
            }
        }
    }
    else
    {
        int k = j-i;
        curA = headA;
        curB = headB;
        while(k--)
        {
            curB = curB->next;//长链表先走k步
        }
        while(curB && curA)
        {
            if(curA == curB)
            {
                return curA;
            }
            else
            {
                curA = curA->next;
                curB = curB->next;
            }
        }
    }
    return NULL;
}


















