tip1:先找到待反转链表的前一个结点,然后把待反转的结点一个个拆下来用头插法构建一个反转的链表,最后把链表接回到原链表中去。
tip2:鉴于这里没有头结点,增加一个空的头结点更方便操作从第一个结点开始反转的情况。
tip3:注意链表尾部置空的问题。
struct ListNode* reverseBetween(struct ListNode* head, int m, int n ) {
    int k  = 1;
    if(m == n)
        return head;  //只反转单个结点相当于不反转
    struct ListNode* phead = (struct ListNode*)malloc(sizeof(struct ListNode));
    //加一个虚拟头结点更方便操作
    phead->next = head;  //指向第一个结点
    struct ListNode *pre = phead, *p = head;  //用pre找到前驱
    struct ListNode *q, *r;
    while(k < m){
        pre = pre->next;  //直到开始反转的前一个结点
        k++;
    }   
    p = pre->next;   //开始反转的结点
    r = pre->next;  //翻转后的接口
    pre->next = NULL;   //拆链后前链的末尾要置空
    struct ListNode * L = NULL;   //为反转链表加一个头指针
    while(k <= n ){  //开始反转
        q = p->next;
        p->next = L;
        L = p;
        p = q;
        k++;
    }
    if(p == NULL)  //一直反转到原链表表尾的情况
       pre->next = L;   //只需要链接前面接口
    else{
        pre->next = L;    //链接前面接口
        r->next = p;    //链接后面接口
    }
    return phead->next;  //无论从哪里开始反转,最后第一个结点都是虚头结点的下一个结点
}
当然,不把结点拆下来反转组装再接回去,而是直接在原链表中通过改变指针来改变其位置更方便。
struct ListNode* reverseBetween(struct ListNode* head, int m, int n ) {
    if( head == NULL || m == n)
        return head;  
    struct ListNode *xuhead = malloc(sizeof(struct ListNode));  //虚头结点
    xuhead->next = head;  //虚头结点的指针域指向第一个结点
    struct ListNode * p = xuhead;  //p指针用来找到第一个反转结点的前驱,初始指空
    for(int i = 0; i<m-1; i++)  //m=1时,p不动;m=1时,p走一步
        p = p->next;  //p停在待反转的结点的前驱,且一直不变
    struct ListNode* r = p->next;  //r指向第一个反转结点
    struct ListNode* tmp;  //用来跟踪后继,以防掉链
    for(int i = 0; i< n-m; i++){  //循环n-m次
        tmp = r->next;  //暂存后继
        r->next = tmp->next;  
        tmp->next = p->next;
        p->next = tmp;
    }
    return xuhead->next;
}
画图更好理解