【编织时空三:探究顺序表与链表的数据之旅】

这篇具有很好参考价值的文章主要介绍了【编织时空三:探究顺序表与链表的数据之旅】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本章重点

  • 链表OJ题

1. 删除链表中等于给定值 val 的所有结点。 OJ链接

思路一:删除头结点时另做考虑(由于头结点没有前一个结点)

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

【编织时空三:探究顺序表与链表的数据之旅】,数据结构,链表,数据结构

思路二:添加一个虚拟头结点,删除头结点就不用另做考虑

struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode* cur = head;
    struct ListNode* newhead = NULL;
    struct ListNode* tail = NULL;
    while (cur != NULL)
    {
        if (cur->val == val)
        {
            struct ListNode* del = cur;
            cur = cur->next;
            free(del);
        }
        else
        {
            //尾插
            if (tail == NULL)
            {
                newhead = tail = cur;
            }
            else
            {
                tail->next = cur;
                tail = tail->next;
            }
            cur = cur->next;
        }
    }
    if (tail)//如果最后一个数是要删除的,tail就需要置空
        tail->next = NULL;
    return newhead;
}

【编织时空三:探究顺序表与链表的数据之旅】,数据结构,链表,数据结构

2. 反转一个单链表。OJ链接

思路:通过三个指针的操作,每次将当前节点反转并向前移动

struct ListNode* reverseList(struct ListNode* head) 
{	
    assert(head);    
    struct ListNode* n1, * n2, * n3;
	n1 = NULL;
	n2 = head;
	n3 = n2->next;
	while (n2)
	{
		//翻转
		n2->next = n1;
		//交换
		n1 = n2;
		n2 = n3;
		//记录位置
        if(n2 != NULL)
		    n3 = n3->next;
	}
	return n1;
}

【编织时空三:探究顺序表与链表的数据之旅】,数据结构,链表,数据结构

思路:头插法

struct ListNode* reverseList(struct ListNode* head) 
{
	struct ListNode* cur = head;
	struct ListNode* newhead = NULL;
	while (cur)
	{
		//保存cur下一个结点的位置
		struct ListNode* next = cur->next;
		//头插
		next = newhead;
		newhead = cur;
		//更新
		cur = next;
	}
	return newhead;
}

【编织时空三:探究顺序表与链表的数据之旅】,数据结构,链表,数据结构

3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则 返回第二个中间结点。OJ链接

思路:快慢指针的前进方向相同,且它们步伐的「差」是恒定的,使用两个指针变量,刚开始都位于链表的第 1 个结点,一个永远一次只走 1 步,一个永远一次只走 2 步,一个在前,一个在后,同时走。这样当快指针走完的时候,慢指针就来到了链表的中间位置。

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

【编织时空三:探究顺序表与链表的数据之旅】,数据结构,链表,数据结构

4. 输入一个链表,输出该链表中倒数第k个结点。OJ链接

首先让快指针先行k步,然后让快慢指针每次同行一步,直到快指针指向空节点,慢指针就是倒数第K个节点。

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k)
{
	struct ListNode* fast = pListHead;
	struct ListNode* slow = pListHead;
	while (k--)//走k步
	{
		//链表没有k步长,那么此时倒数就是空
		if (fast == NULL)
			return NULL;
		fast = fast->next;
	}
	while (fast)
	{
		slow = slow->next;
		fast = fast->next;
	}
	return slow;
}

【编织时空三:探究顺序表与链表的数据之旅】,数据结构,链表,数据结构

5. 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有结点组成的。OJ链接

思路一:我们可以用迭代的方法来实现上述算法。当 l1 和 l2 都不是空链表时,判断 l1 和 l2 哪一个链表的头节点的值更小,将较小值的节点添加到结果里,当一个节点被添加到结果里之后,将对应链表中的节点向后移一位。

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) 
{
	if (list1 == NULL)
		return list2;
	if (list2 == NULL)
		return list1;
	struct ListNode* newhead = NULL;
	struct ListNode* tail = NULL;
	while (list1 && list2)
	{
		//小值给到新链表上
		if (list1->val < list2->val)
		{
			if (tail == NULL)
			{
				newhead = tail = list1;
			}
			else
			{
				tail->next = list1;
				tail = tail->next;
			}
			list1 = list1->next;
		}
		else
		{
			if (tail == NULL)
			{
				newhead = tail = list2;
			}
			else
			{
				tail->next = list2;
				tail = tail->next;
			}
			list2 = list2->next;
		}
	}
	if (list1)
		tail->next = list1;
	if (list2)
		tail->next = list2;
	return newhead;
}

【编织时空三:探究顺序表与链表的数据之旅】,数据结构,链表,数据结构

思路二:哨兵位法,创建一个带头结点的链表,尾插的时候就不需要判断链表是不是为空的尾插情况,最后再释放哨兵位即可。

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) 
{
	if (list1 == NULL)
		return list2;
	if (list2 == NULL)
		return list1;
	struct ListNode* newhead = NULL;
	struct ListNode* tail = NULL;
	//哨兵位,方便尾插
	newhead = tail = (struct ListNode*)malloc(sizeof(struct ListNode));
	while (list1 && list2)
	{
		//小值给到新链表上
		if (list1->val < list2->val)
		{
			tail->next = list1;
			tail = tail->next;
			list1 = list1->next;
		}
		else
		{
			tail->next = list2;
			tail = tail->next;
			list2 = list2->next;
		}
	}
	if (list1)
		tail->next = list1;
	if (list2)
		tail->next = list2;
	struct ListNode* del = newhead;
	newhead = newhead->next;
	//释放哨兵位
	free(del);
	return newhead;
}

6. 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。OJ链接

思路:首先创建四个节点lessHead,greaterHead,lessTail,greaterTail ,遍历整个链表,比x小的尾插到lessHead为哨兵位的那个链表,比x大的尾插到greaterHead为哨兵位的那个链表,再把两个链表连接起来 ,创建一个list节点指向这个链表 ,把greaterTail->next置空,避免成环 ,释放lessHead,greaterHead,返回list 

struct ListNode* partition(struct ListNode* pHead, int x)
{
    struct ListNode* lessHead, * greaterHead;
    lessHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    greaterHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* lessTail = lessHead;
    struct ListNode* greaterTail = greaterHead;
    struct ListNode* cur = pHead;
    while (cur)
    {
        if (cur->val < x)
        {
            lessTail->next = cur;
            lessTail = lessTail->next;
        }
        else
        {
            greaterTail->next = cur;
            greaterTail = greaterTail->next;
        }
        cur = cur->next;
    }
    lessTail->next = greaterHead->next;

    //此时greaterTail->next仍然链接初始链表的结点,需要置空,否则连环
    greaterTail->next = NULL;
    
    struct ListNode* newhead = lessHead->next;
    free(lessHead);
    free(greaterHead);

    return newhead;
}

【编织时空三:探究顺序表与链表的数据之旅】,数据结构,链表,数据结构

7. 链表的回文结构。OJ链接

思路:先找到链表的中间结点,再把中间结点之后的逆序,和之前的链表比较值是否相等。

struct ListNode* middleNode(struct ListNode* head)//找中间结点
{
	struct ListNode* fast = head;
	struct ListNode* slow = head;
	while (fast && fast->next)
	{
		slow = slow->next;
		fast = fast->next->next;
	}
	return slow;
}
struct ListNode* reverseList(struct ListNode* head)//反转链表
{
	struct ListNode* cur = head;
	struct ListNode* newhead = NULL;
	while (cur)
	{
		//保存cur下一个结点的位置
		struct ListNode* next = cur->next;
		//头插
		next = newhead;
		newhead = cur;
		//更新
		cur = next;
	}
	return newhead;
}
bool chkPalindrome(struct ListNode* A) //查看值是否相等
{
	struct ListNode* midnode = middleNode(A);
	struct ListNode* reversemidnode = reverseList(midnode);
	while (A && reversemidnode)
	{
		if(A->val != reversemidnode->val)
		{
			return false;
		}
		A = A->next;
		reversemidnode = reversemidnode->next;
	}
	return true;
}

【编织时空三:探究顺序表与链表的数据之旅】,数据结构,链表,数据结构

8. 输入两个链表,找出它们的第一个公共结点。OJ链接

思路:先计算两个链表的长度,再让较长的链表走差距步abs(lenA-LenB)长度,然后再依次比较是否相等。

struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB) 
{
	struct ListNode* curA = headA, * curB = headB;
	//找尾结点 - 结点总数会少一个
	int lenA = 1;//设置为1
	int lenB = 1;//设置为1
	while (curA->next)
	{
		curA = curA->next;
		lenA++;
	}
	while (curB->next)
	{
		curB = curB->next;
		lenB++;
	}
	//两个链表不相交
	if (curA != curB)
		return NULL;

	//找长链表
	struct ListNode* LongList = headA, * ShortList = headB;
	if (lenA < lenB)
	{
		LongList = headB;
		ShortList = headA;
	}

	//长链表走绝对值(lenA - lenB)步
	int count = abs(lenA - lenB);
	while (count--)
	{
		LongList = LongList->next;
	}

	//同时向后走,相同就停下来
	while (LongList != ShortList)
	{
		LongList = LongList->next;
		ShortList = ShortList->next;
	}
	return ShortList;
}

9. 给定一个链表,判断链表中是否有环。OJ链接

思路:快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针从链表其实位置开始运行, 如果链表 环则一定会在环中相遇,否则快指针率先走到链表的末尾。

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

        if(fast == slow)
           return true;
    }
    return false;
}

【编织时空三:探究顺序表与链表的数据之旅】,数据结构,链表,数据结构

 为什么快指针每次走两步,慢指针走一步可以?

  • 假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚 进环时,可能就和快指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。 此时,两个指针每移动一次,快指针走两次,慢指针走一次,之间的距离就缩小一步,直至最后差距为0,不会出现每次刚好是套圈的情况,因此:在满指针走到一圈之前,快指针肯定是可以追上慢指针的,即相遇。

快指针一次走3步,走4步,...n步行吗?

  • 【编织时空三:探究顺序表与链表的数据之旅】,数据结构,链表,数据结构
  • 【编织时空三:探究顺序表与链表的数据之旅】,数据结构,链表,数据结构

10. 给定一个链表,返回链表开始入环的第一个结点。 如果链表无环,则返回 NULL。OJ链接

思路一:一个指针从链表起始位置运行,一个指针从相遇点位置绕环,每次都走一步,两个指针最终会在入口点的位置相遇

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode *fast = head,*slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fase->next->next;

        //相遇在环内任意一个位置
        if(slow == fast)
        {
            struct ListNode *meet = slow;
            //两结点关系L = (N-1) * C + C - X;
            while(head != meet)
            {
                head = head->next;
                meet = meet->next;
            }
            return meet;
        }
    }
    return NULL;
}

​

【编织时空三:探究顺序表与链表的数据之旅】,数据结构,链表,数据结构

 思路二:两个指针相遇的地方的下一个结点置空,下一个结点位置和链表头指针此时就可以转为两条链表求解公共点的问题。

struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB)//相交点 
{
	struct ListNode* curA = headA, * curB = headB;
	//找尾结点 - 结点总数会少一个
	int lenA = 1;//设置为1
	int lenB = 1;//设置为1
	while (curA->next)
	{
		curA = curA->next;
		lenA++;
	}
	while (curB->next)
	{
		curB = curB->next;
		lenB++;
	}
	//两个链表不相交
	if (curA != curB)
		return NULL;

	//找长链表
	struct ListNode* LongList = headA, * ShortList = headB;
	if (lenA < lenB)
	{
		LongList = headB;
		ShortList = headA;
	}

	//长链表走绝对值(lenA - lenB)步
	int count = abs(lenA - lenB);
	while (count--)
	{
		LongList = LongList->next;
	}

	//同时向后走,相同就停下来
	while (LongList != ShortList)
	{
		LongList = LongList->next;
		ShortList = ShortList->next;
	}
	return ShortList;
}
struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode *fast = head,*slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fase->next->next;

        //相遇在环内任意一个位置
        if(slow == fast)
        {
            struct ListNode *meet = slow;
            struct ListNode *newhead = meet->next;
            meet->next = NULL;
            return getIntersectionNode(head,newhead);
        }
    }
    return NULL;
}

11.给定一个链表,每个结点包含一个额外增加的随机指针,该指针可以指向链表中的任何结点或空结点。 要求返回这个链表的深度拷贝。OJ链接

思路:迭代 + 节点拆分

方法:

【编织时空三:探究顺序表与链表的数据之旅】,数据结构,链表,数据结构

struct Node* copyRandomList(struct Node* head) {
	struct Node* cur =  head;
    while(cur)
    {
        struct Node* next = cur->next;
        struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
        copy->val = cur->val;

        //插入
        cur->next = copy;
        copy->next = next;

        //往后走
        cur = next;
    }

    cur = head;
    while(cur)
    {
        struct Node* copy = cur->next;

        //置 copy random
        if(cur->random == NULL)
            copy->random = NULL;
        else
            copy->random = cur->random->next;
        cur = copy->next;
    }

    cur = head;
    struct Node* copyhead = NULL,*cpoytail = NULL;
    while(cur)
    {
        struct Node* copy = cur->next;
        struct Node* next = copy->next;

        //copy结点尾插到新链表
        if(cpoytail == NULL)
        {
            copyhead = copytail = copy;
        }
        else
        {
            copytail->next = copy;
            copytail = copytail->next;
        }

        //恢复原链表
        cur->next = next;
        cur = next;
    }
    return copyhead;
}

【编织时空三:探究顺序表与链表的数据之旅】,数据结构,链表,数据结构

 本章结束啦!!!

【编织时空三:探究顺序表与链表的数据之旅】,数据结构,链表,数据结构文章来源地址https://www.toymoban.com/news/detail-677759.html

到了这里,关于【编织时空三:探究顺序表与链表的数据之旅】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构与算法】顺序表与链表(单链表和双链表)超详解图示与源码。

                                                       大家好,今天我们来学习数据结构中的顺序表与链表!源码在最后附上 首先我们先来认识一下 顺序表 :                                       **如上图所示:很多人会以为数组就是顺序表,顺序表就是数组,这

    2024年02月21日
    浏览(59)
  • 【数据结构 | 入门】线性表与链表 (问题引入&实现&算法优化)

    🤵‍♂️ 个人主页: @计算机魔术师 👨‍💻 作者简介:CSDN内容合伙人,全栈领域优质创作者。 本文是浙大数据结构学习笔记专栏 这里我们引入一个问题,最常见的多项式,我们如何使用编程将多项式表示出来呢? 我们可以使用数组来表示,但是会随着一个问题,如下图底

    2024年01月21日
    浏览(71)
  • C语言链表的含义与链表数据操作代码详解!

            在讲解开始前,我们先来看一张图片:         如图我们可以看到一列火车,它由车头和车厢组成,同时由链条连接,从整个火车我们可以看出,前一节的车厢尾总有着一个链条,让它紧密与后一个车厢相连。这样,如果我们找到了前一个车厢,那么我们就可以同

    2024年04月23日
    浏览(40)
  • C语言双向链表的含义与链表数据操作代码详解!

    引言: 于本文中,我们将讲到C语言数据结构中的双向链表的含义,以及对于双向链表的增删查改函数。该函数相比于单向链表,反而还更加的简单。希望这篇文章可以对你有帮助,也希望同样热爱代码编程的你能给我支持,您的支持就是我最大的动力! 关于顺序表以及单链

    2024年04月17日
    浏览(39)
  • 【数据结构与算法分析】反转链表与顺序表(内含源码,思路清晰)

      顺序表和链表都是数据结构中常见的线性表。它们的主要区别在于 内存管理方式不同 。   顺序表(Array)是由一系列元素按照一定顺序依次排列而成,它使用连续的内存空间存储数据。顺序表使用一个数组来存储数据,数组中的每个元素都可以通过下标来访问。顺序

    2024年02月07日
    浏览(105)
  • 数组与链表的区别

    数组是连续存储,数组在创建时需要一个整块的空间。 链表是链式存储,链表在内存空间中不一定是连续的。 数组一般创建在栈区,链表一般创建在堆区,在增加节点时需要new或malloc新节点,相较于数组长度不固定,自由度高。 数组可以通过下标随机访问,单向链表只能通

    2024年02月05日
    浏览(74)
  • 数据结构(王道)——线性表之静态链表&顺序表和链表的比较

      如何定义一个静态链表     初始化静态链表:   静态链表的查找、插入、删除           创: 销:   增、删:   查:   顺序表、链表该如何选择?  

    2024年02月16日
    浏览(126)
  • <数据结构>顺序表和链表的比较|缓存命中率

    💭前言:通过之前对顺序表和链表的实现,我们可以发现在增删查改某些操作上两者的效率前言有所差异,本篇文章将总结二者的异同。 顺序表的实现 http://t.csdn.cn/Lxyg2 单链表的实现 http://t.csdn.cn/rHgjG 双链表的实现 http://t.csdn.cn/j3amO 📚顺序表通过数组来实现的,所以在物理

    2024年02月05日
    浏览(52)
  • 数据结构实验---顺序表的合并---链表的基本操作---重点解析约瑟夫问题

    实验的写法多种多样,但本文并未采用 #define 定义容量的写法,这样写已经是很老旧过时的写法。所有实验主体采用均为动态开辟,后续如果利用 C++ 来写或许会应用更多语法… 本篇展示数据结构的两个实验 其中,重点分析约瑟夫问题 实验中代码的命名风格等均与下方博客

    2024年02月16日
    浏览(62)
  • C语言---数据结构实验---顺序表的合并---链表的基本操作---重点解析约瑟夫问题

    实验的写法多种多样,但本文并未采用 #define 定义容量的写法,这样写已经是很老旧过时的写法。所有实验主体采用均为动态开辟,后续如果利用 C++ 来写或许会应用更多语法… 本篇展示数据结构的两个实验 其中,重点分析约瑟夫问题 实验中代码的命名风格等均与下方博客

    2024年02月16日
    浏览(70)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包