初阶数据结构——链表习题

目录

  • 如何快速调试链表习题
  • 链表习题
    • 移除链表元素
    • 链表的中间节点
    • 反转链表
    • 链表中倒数第k个结点
    • 合并两个有序链表
    • 链表分割
    • 链表的回文结构
    • 相交链表
    • 环形链表
      • slow和fast一定会相遇吗?如果fast一次走(3、4、5)步呢?
    • 环形链表 Ⅱ
      • 思路1:
      • 推论
      • 思路2:
    • 复制带随机指针的链表

如何快速调试链表习题

int main()
{
	struct ListNode* n1 = (struct ListNode*)malloc(sizeof(struct ListNode));
	assert(n1);
	struct ListNode* n2 = (struct ListNode*)malloc(sizeof(struct ListNode));
	assert(n2);
	struct ListNode* n3 = (struct ListNode*)malloc(sizeof(struct ListNode));
	assert(n3);
	struct ListNode* n4 = (struct ListNode*)malloc(sizeof(struct ListNode));
	assert(n4);
	n1->val = 7;
	n2->val = 7;
	n3->val = 7;
	n4->val = 7;

	n1->next = n2;
	n2->next = n3;
	n3->next = n4;
	n4->next = NULL;

	n1 = removeElements(n1, 7);

	return 0;
}

链表习题

移除链表元素

OJ链接
初阶数据结构——链表习题_第1张图片
方法1:遇到val就删除

struct ListNode* removeElements(struct ListNode* head, int val) {
	struct ListNode* prev = NULL, * cur = head;
	while (cur)
	{
		if (cur->val == val)
		{
            if(prev==NULL)
            {
                cur=head->next;
                free(head);
                head=cur;
            }
            else
            {
                prev->next = cur->next;
			    free(cur);
			    cur = prev->next;
            }
		}
		else
		{
			prev = cur;
			cur = cur->next;
		}
	}

	return head;
}

方法2:遍历当前链表,把不是val的拿下来尾插

struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode*phead=NULL,*tail=NULL;
    while(head)
    {
        if(head->val!=val)
        {
            if(tail==NULL)
            {
                phead=tail=head;
            }
            else
            {
                tail->next=head;
                tail=tail->next;
            }
        }
        //printf("%d\n",head->val);
        head=head->next;
        if(tail)
        tail->next=NULL;
    }
    return phead;
}

链表的中间节点

OJ链接
初阶数据结构——链表习题_第2张图片

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* middleNode(struct ListNode* head){
    struct ListNode*slow=head,*fast=head;
    while(fast && fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
    }
    return slow;
}

反转链表

OJ链接
初阶数据结构——链表习题_第3张图片
初阶数据结构——链表习题_第4张图片
用prev,cur,next也可以

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* reverseList(struct ListNode* head){
    if(head==NULL || head->next==NULL)
    return head;
    struct ListNode*prev=head,*cur=head->next,*next=head->next->next;
    prev->next=NULL;
    while(cur)
    {
        cur->next=prev;
        prev=cur;
        cur=next;
        if(next)
        next=next->next;
    }
    return prev;
}

链表中倒数第k个结点

OJ链接
初阶数据结构——链表习题_第5张图片

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */

/**
 * 
 * @param pListHead ListNode类 
 * @param k int整型 
 * @return ListNode类
 */
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    struct ListNode*cur=pListHead;
    struct ListNode*next=pListHead;
    while(k-- && next)
    {
        next=next->next;
    }
    if(k!=-1)
    return NULL;
    while(next!=NULL)
    {
        cur=cur->next;
        next=next->next;
    }
    //printf("%d\n\n",cur->val);
    return cur;
}

合并两个有序链表

OJ链接
初阶数据结构——链表习题_第6张图片
不创建哨兵位的头节点的话:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    struct ListNode*head=NULL,*tail=NULL;
    if(list1==NULL)
    return list2;
    if(list2==NULL)
    return list1;
    
    while(list1 && list2)
    {
        if(list1->val>list2->val)
        {
            if(tail==NULL)
            {
                head=tail=list2;
            }
            else
            {
                tail->next=list2;
                tail=tail->next;
            }
            list2=list2->next;
        }
        else
        {
            if(tail==NULL)
            {
                head=tail=list1;
            }
            else
            {
                tail->next=list1;
                tail=tail->next;
            }
            list1=list1->next;
        }
    }
    if(list1)
    tail->next=list1;
    if(list2)
    tail->next=list2;

    return head;
}

也可以选择创建一个哨兵位的头节点来方便处理尾插

初阶数据结构——链表习题_第7张图片
从图可以看出我们尾插的时候会记录尾tail,不然每次找尾效率太低,那么写单链表时为什么没有用tail呢?因为加尾指针也是有代价的,传给函数的时候就不是单单传头指针了,需要传结构体,结构体中有头指针和尾指针,结构复杂的话不利于学习链表。而且加了尾指针也只是方便了尾插,但对尾删没有帮助。

哨兵位的头节点一般不存储有效数据
有一些地方可能会用头节点存链表长度,其实是不好的,如果链表的数据类型是整形那么是好的,但当数据类型不是int,是char的时候,链表长度超过128的时候,记录的链表的长度就离谱起来了
初阶数据结构——链表习题_第8张图片

当写单链表时用带哨兵位的头节点会写的更容易,不需要二级指针,因为头指针一直指向的是哨兵位,头指针的指向不需要更改了。

链表分割

OJ链接
初阶数据结构——链表习题_第9张图片

思路:
初阶数据结构——链表习题_第10张图片
易错点:

6的指向记得要指向NULL,否则会死循环
初阶数据结构——链表习题_第11张图片
推荐使用带哨兵位的头节点,这样会方便很多

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        struct ListNode*lesshead,*lesstail,*greaterhead,*greatertail,*cur;
        lesshead=NULL;
        lesstail=NULL;
        greaterhead=NULL;
        greatertail=NULL;
        if(pHead==NULL)
        {
            return NULL;
        }
        cur=pHead;
        while(cur)
        {
            if(cur->val<x)
            {
                if(lesshead==NULL)
                {
                    lesshead=lesstail=cur;
                }
                else
                {
                    lesstail->next=cur;
                    lesstail=cur;
                }
            }
             else
            {
                if(greaterhead==NULL)
                {
                    greaterhead=greatertail=cur;
                }
                else
                {
                    greatertail->next=cur;
                    greatertail=cur;
                }
                 
            }
            cur=cur->next;
        }
        if(lesstail!=NULL)
         {
              lesstail->next=greaterhead;
         }
        if(greatertail!=NULL)
        {
        greatertail->next=NULL;
        }
        if(lesshead!=NULL)
        return lesshead;
        else
        return greaterhead;
    }
};

如果不使用带哨兵位的头节点,会有点麻烦,遇到第一个链表为空时,第二个链表为空时,尾插时都要多注意。

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        struct ListNode* headleft = pHead, * headright = pHead, * tailleft = NULL, * tailright = NULL;
    //printf("%d\n", headleft->val);
    while (pHead)
    {
        if (pHead->val < x)
        {
            if (tailleft == NULL)
            {
                headleft = tailleft = pHead;
            }
            else
            {
                tailleft->next = pHead;
                tailleft = tailleft->next;
            }
            pHead = pHead->next;
            if (tailleft)
                tailleft->next = NULL;
        }
        else
        {
            if (tailright == NULL)
            {
                headright = tailright = pHead;
            }
            else
            {
                tailright->next = pHead;
                tailright = tailright->next;
            }
            pHead = pHead->next;
            if (tailright)
                tailright->next = NULL;
        }
    }
    if (tailleft)
    {
        if(tailright)
        tailleft->next = headright;
        return headleft;
    }
    else
    {
        return headright;
    }
    }
};

链表的回文结构

OJ链接
初阶数据结构——链表习题_第12张图片
思路:找中间结点,然后以中间结点为头,将后面链表逆置
注意:逆置后3是指向NULL,但2的next还是3
初阶数据结构——链表习题_第13张图片

class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) {
        if(A==NULL)
        return false;
        //快慢指针找中间
        struct ListNode*slow=A,*fast=A;
        while(fast && fast->next)
        {
            slow=slow->next;
            fast=fast->next->next;
        }
        //将中间结点和后面结点逆置
        //1->2->3->2->1
        //逆置后:3中间结点的next会为NULL,2的next没有变,还是指向3
        //1->2->1->2->3
        struct ListNode*prev=NULL,*cur=slow,*next=slow->next;
        while(cur)
        {
            cur->next=prev;
            prev=cur;
            cur=next;

            if(next)
            next=next->next;
        }
        struct ListNode*head=A;
        while(prev)//不能用head作为结束条件,因为head为空时prev就会存在空指针访问
        {
            if(head->val!=prev->val)
            {
                return false;
            }
            else
            {
                head=head->next;
                prev=prev->next;
            }
        }
        return true;
    }
};

相交链表

OJ链接
初阶数据结构——链表习题_第14张图片
思路:
初阶数据结构——链表习题_第15张图片

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode*curA=headA,*curB=headB;
    int lenA=1;
    int lenB=1;
    while(curA->next)
    {
        curA=curA->next;
        lenA++;
    }
    while(curB->next)
    {
        curB=curB->next;
        lenB++;
    }
    if(curA!=curB)
    return NULL;

    //长链表先走差距步
    struct ListNode*longList=headA;
    struct ListNode*shortList=headB;
    if(lenB>lenA)
    {
        longList=headB;
        shortList=headA;
    }
    int gap=abs(lenA-lenB);
    while(gap--)
    {
        longList=longList->next;
    }
    while(longList!=shortList)
    {
        longList=longList->next;
        shortList=shortList->next;
    }
    return longList;
}

环形链表

OJ链接
初阶数据结构——链表习题_第16张图片
思路:
fast一次走两步,slow一次走一步,fast和slow相遇,就说明存在环

bool hasCycle(struct ListNode *head) {
    struct ListNode*slow=head,*fast=head;
    while(fast && fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
        {
            return true;
        }
    }

    return false;
}

slow和fast一定会相遇吗?如果fast一次走(3、4、5)步呢?

看fast一次走几步
fast一次走两步则一定相遇
初阶数据结构——链表习题_第17张图片
fast一次走三步则不一定相遇
初阶数据结构——链表习题_第18张图片

环形链表 Ⅱ

OJ链接
初阶数据结构——链表习题_第19张图片

思路1:

一个指针从相遇点走,一个指针从链表头开始走,他们会在入口点相遇

初阶数据结构——链表习题_第20张图片

推论

初阶数据结构——链表习题_第21张图片

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode*slow=head,*fast=head;
    while(fast && fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
        {
            struct ListNode*phead=head;
            while(slow!=phead)
            {
                slow=slow->next;
                phead=phead->next;
            }
            return phead;
        }
    }
    return NULL;
}

思路2:

初阶数据结构——链表习题_第22张图片

复制带随机指针的链表

OJ链接
初阶数据结构——链表习题_第23张图片
思路
初阶数据结构——链表习题_第24张图片

struct Node* copyRandomList(struct Node* head) {
    if(head==NULL)
    {
        return NULL;
    }
	//拷贝节点到原节点的后面
    struct Node*cur=head;
    struct Node*copy=NULL;
    while(cur)
    {
        copy=(struct Node*)malloc((sizeof(struct Node)));
        copy->val=cur->val;
        
        copy->next=cur->next;
        cur->next=copy;
        cur=copy->next;
    }

    //控制拷贝节点的random使其指向原节点random的next
    cur=head;
    copy=cur->next;
    while(cur)
    {
        copy=cur->next;
        if(cur->random!=NULL)
        copy->random=cur->random->next;
        else
        copy->random=NULL;

        cur=copy->next;
    }

    //恢复原链表
    cur=head;
    struct Node*copyhead=copy,*copytail=NULL;
    while(cur)
    {
        copy=cur->next;
        if(copytail==NULL)
        {
            copytail=copyhead=copy;
        }
        else
        {
            copytail->next=copy;
            copytail=copytail->next;
        }
        cur->next=copy->next;
        cur=copy->next;
    }
    return copyhead;
}

你可能感兴趣的:(数据结构初阶学习笔记,链表,数据结构)