算法通关村第二关--白银挑战

1.学习目的

1.复习昨天所学链表反转
2…学习今天的链表反转的拓展问题

2.学习的代码和思考

2.1链表反转之反思

(1)具有虚拟节点的反转
思路:利用好虚拟节点来改变指针指向
相关代码:首先初始化一个头节点ant(起到前驱结点的作用),然后定义一个指向当前节点的指针cur,最后在cur!=NULL时不断循环反转指针
next=cur->next;//定义后继结点
cur->next=ant->next;//断开当前结点的指向
ant->next=cur;//让其前驱结点指向当前节点
cur=next;//结点后移
(2)没有虚拟节点的反转
思路:与上述思路类似,只不过将前驱节点换成了一个指针pre,利用指针来实现,即把cur-》next换为pre即可
next=cur->next;//定义后继结点
cur->next=pre;//断开当前结点的指向
pre=cur;//让其前驱结点指向当前节点
cur=next;//结点后移
限制条件依旧相同;
过程图如下:
算法通关村第二关--白银挑战_第1张图片
代码:

struct ListNode* reverseList(struct ListNode* head){
    struct  ListNode* prev = NULL;
    struct  ListNode* curr = head;

    while (curr != NULL) {
       struct ListNode* next = curr->next;
        curr->next = prev;
        prev = curr;
        curr = next;
    }

    return prev;
}

可以发现,带和不带虚拟节点就时差了个分配内存

2.2指定区间的反转

思路(未看解析前):反转操作依旧相同,只不过需要把限制条件和cur指针指向改变一下
看了之后:跟想的不一样,单纯反转指针需要改变很多指针指向,很麻烦,故此需要头插法或者插针引线发来解决
头插法:不断将next指向结点前插到pre就

struct ListNode *reverseBetween(struct ListNode *head, int left, int right)
{
    struct ListNode *dummyNode = (struct ListNode *)malloc(sizeof(struct ListNode));
    dummyNode->next = head;//存储head地址,防止后续地址丢失,无法进行操作
    struct ListNode *pre = dummyNode;

    for (int i = 0; i < left - 1; i++)//找到需要反转位置前一个结点
    {
        pre = pre->next;
    }

    struct ListNode *cur = pre->next;//当前节点
    struct ListNode *next;//后继结点

    for (int i = 0; i < right - left; i++)//将next不断头插到pre后面,插的次数便是right-left
    {
        next = cur->next;
        cur->next = next->next;//断开next与cur的连线
        next->next = pre->next;//断开next与其后继节点的连线,连接到cur上
        pre->next = next;//pre链接cur
    }
//注意:上述操作中cur,pre指向的节点不变
    struct ListNode *newHead = dummyNode->next;//newNode为第一个值的指针域
    free(dummyNode);//释放内存

    return newHead;
}

流程图(自画):
算法通关村第二关--白银挑战_第2张图片
穿针引线法:是最开始的思路上面,然后多加两个指针,完成了改变指针方向的问题,注意头节点的定义

void reverseLinkedList(struct ListNode *head)
{
    struct ListNode *pre = NULL;
    struct ListNode *cur = head;
    while (cur != NULL)
    {
        struct ListNode *next = cur->next;
        cur->next = pre;
        pre = cur;
        cur = next;
    }
}

struct ListNode *reverseBetween2(struct ListNode *head, int left, int right)
{
//    struct ListNode *dummyNode = (struct ListNode *)malloc(sizeof(struct ListNode));
//    dummyNode->next = head;
    struct ListNode *dummyNode;
    dummyNode->next=head; 
    struct ListNode *pre = dummyNode;//反转函数接收的结点为反转后的头节点 ,

    for (int i = 0; i < left - 1; i++)
    {
        pre = pre->next;//找到待反转的结点的前一个结点
    }

    struct ListNode *rightNode = pre;
    for (int i = 0; i < right - left + 1; i++)
    {
        rightNode = rightNode->next;
    }

    struct ListNode *leftNode = pre->next;//定义待反转子链表中的左右结点
    struct ListNode *succ = rightNode->next;

    pre->next = NULL;//断开pre连接的leftnode 
    rightNode->next = NULL;//断开rightnode 

    reverseLinkedList(leftNode);

    pre->next = rightNode;//重连 
    leftNode->next = succ;

    return dummyNode;
}



int main()
{
    struct ListNode *p = NULL;
    printf("create list: \t\n");

    int a[] = {1, 2, 3, 4, 5};
    p = initLink(a, 5);
    struct ListNode *q = reverseBetween2(p, 2, 4);
    printList(q);//输出头节点地址

    return 0;
}

运行结果:
算法通关村第二关--白银挑战_第3张图片

结束了

2.3两两交换链表中的结点

流程图:
算法通关村第二关--白银挑战_第4张图片
相关代码及运行截图:

  struct ListNode * swapPairs(struct ListNode * head)
{//建立虚拟结点并将头结点赋值给临时指针
    struct ListNode *dummyHead = (struct ListNode *)malloc(sizeof(struct ListNode));
    dummyHead->next = head;
    struct ListNode *temp = dummyHead;

    while (temp->next != NULL && temp->next->next != NULL)
    {
        struct ListNode *node1 = temp->next;
        struct ListNode *node2 = temp->next->next;
        temp->next = node2;
        node1->next = node2->next;
        node2->next = node1;
        temp = node1;
    }

    //struct ListNode *newHead = dummyHead->next;
    //free(dummyHead);
    return dummyHead->next;//这里定义一个头指针是为了释放内存 ,不释放也可以 
}

运行截图:
算法通关村第二关--白银挑战_第5张图片
注意:**创建时head->val=1;**这里返回的head即是所给数组的1的指针域,一定一定要注意!!!!!!!!
2.4单链表加1
人已经死了,回头再做这个

3.总结与反思

链表还是不太行,等回头回来再看一遍后面的题目,现在实在是太费脑子了,下周六周日复习的时候顺带把剩余的几题搞了

你可能感兴趣的:(算法)