当前位置: 首页 > news >正文

【数据结构入门】单链表和数组的OJ题(1)

目录

1.移除链表元素

分析:

代码: 

2.链表的中间结点

分析:

 代码:

3.链表中倒数第k个结点

分析:

代码:

4. 合并两个有序链表

分析: 

代码:

5.分割链表

分析:

代码:


1.移除链表元素

在原链表中直接修改

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

示例 1:

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:

输入:head = [], val = 1
输出:[]

示例 3:

输入:head = [7,7,7,7], val = 7
输出:[]

分析:

        ①这里需要两个指针,prev、curr,分别初始化为NULL、head;如果要删除curr所指向的元素,直接使用prev跳过curr即可。

        ②循环条件是curr不为空,当curr找到val的时候,此时分两种情况

                (1)curr就在首元素:此时将head重新赋值为curr的下一个元素,然后释放掉curr,更新curr为head;

                (2)curr指向链表非首元素位置,prev此时就不是空,此时需要prev指向curr的下一个元素,释放curr,然后将curr更新为prev;

        ③当curr没找到val的时候,此时直接先更新prev为curr,在更新curr为curr的next。

代码: 

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
struct ListNode* removeElements(struct ListNode* head, int val) {struct ListNode* prev = NULL;struct ListNode* curr = head;if(head == NULL){return head;}while (curr) {if (curr->val == val) {// 如果第一个就是要删除的if(curr == head){head = curr->next;free(curr);curr = head;}else{   // 正常情况prev->next = curr->next;free(curr);curr = prev->next;}}else{prev = curr; // 始终在curr之前curr = curr->next;}}return head;
}

2.链表的中间结点

给你单链表的头结点 head ,请你找出并返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

示例 1:

输入:head = [1,2,3,4,5]
输出:[3,4,5]
解释:链表只有一个中间结点,值为 3 。

示例 2:

输入:head = [1,2,3,4,5,6]
输出:[4,5,6]
解释:该链表有两个中间结点,值分别为 3 和 4 ,返回第二个结点。

分析:

        使用最简单的方式是:先遍历一边拿到总结点个数,根据总节点个数算出中间节点,最后遍历一遍得到中间节点。

        有没有更好的方法呢? 能不能只遍历一次就找到中间节点呢?可以使用快慢指针,快指针一次走两步,慢指针一次走一步,快指针走到尾的时候,慢指针刚好在中间。

 

细节:如果是奇数,那么快指针走到最后一个节点结束 ,如果是偶数,快指针走到空结束

停止条件:快指针走到最后一个或者快指针走到空这个循环就结束;

继续条件:快指针走到最后一个并且快指针走到空这个循环就结束。

即:

while(fast && fast->next)

 代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/struct ListNode* middleNode(struct ListNode* head) {struct ListNode* fast = head;struct ListNode* slow = head;while(fast && fast->next){fast = fast->next->next;slow = slow->next;}return slow;
}

3.链表中倒数第k个结点

牛客网传送门

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

示例1

输入

1,{1,2,3,4,5}

输出

{5}

分析:

        这里能不能使用双指针,只遍历一次就能完成任务呢?首先让快指针先走k次,然后和慢指针同步走,直到快指针走到空,此时慢指针的位置就是倒数第k个的位置。

        原理其实就是让快指针和慢指针保持k个距离,当快指针为空的时候,慢指针就是倒数第k个。

        需要注意的事牛客网的测试用例有可能k>链表长度,这就会让链表在走k个元素的时候发生空指针解引用的问题,所以每次k往前走的时候都需要判断k不为空,如果在向前走k步的过程中发现快指针为空了,那么说明此时链表的长度小于k,直接返回空即可。

代码:

/*** 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* fast = pListHead;struct ListNode* slow = pListHead;// 先让fast走k步while (k--) {if (fast != NULL) {fast = fast->next;} else {return NULL;}}while (fast) {// 再一起走fast = fast->next;slow = slow->next;}// fast为空的时候slow指向倒数第k个return slow;
}

4. 合并两个有序链表

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

示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1 和 l2 均按 非递减顺序 排列
  • 分析: 

  •         首先使用一个全新的链表来作为返回链表,这里定义一个链表头和链表指针,根据l1指针和l2指针所指向的值的大小来判断,新链表中是否要增加新节点。

    ①判断l1是否为空,为空,返回l2,反之亦如此;

    ②给新链表的头结点赋初值,这里将l1或者l2指针中较小的元素赋给新链表的头节点、遍历指针,赋值的指针需要向后移动,否则后面的循环就是死循环(自己指向自己);

    ③循环的条件要保证两个链表均不为空,若有一个链表为空,那么直接将另外一个链表接到新链表后面。

  • ④循环体内判断l1还是l2所指向的节点更小,将更小的节点赋给新链表指针指向的位置,此时需要更新新链表指针和更小的节点所在的链表的指针;

  • ⑤循环结束之后,判断是哪一个链表为空,将另一个链表接到新链表后面即可

  • ⑥返回新链表的头结点。

  • 代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {struct ListNode* curr1 = list1;struct ListNode* curr2 = list2;struct ListNode* head = NULL;struct ListNode* newcurr = head;if (curr1 == NULL) {return curr2;} else if(curr2 == NULL) {return curr1;}// 初始化头if (curr1->val < curr2->val) {head = curr1;newcurr = head;curr1 = curr1->next;} else {head = curr2;newcurr = head;curr2 = curr2->next;}// 停止条件:两个指针有一个为空while (curr1 && curr2) {if (curr1->val < curr2->val) {newcurr->next = curr1;curr1 = curr1->next;} else {newcurr->next = curr2;curr2 = curr2->next;}newcurr = newcurr->next;}if (curr1 == NULL) {newcurr->next = curr2;} else {newcurr->next = curr1;}return head;
}

5.分割链表

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你不需要 保留 每个分区中各节点的初始相对位置。

示例 1:

输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]

示例 2:

输入:head = [2,1], x = 2
输出:[1,2]

提示:

  • 链表中节点的数目在范围 [0, 200] 内
  • -100 <= Node.val <= 100
  • -200 <= x <= 200

分析:

        可以使用两个带头结点的链表来分别保存大于x的节点和小于x的节点,最后合并这两个节点即可。

①首先创建带头节点的两个链表,这里的头结点不存实际的值,只是为了逻辑上更简单不需要判断链表是否为空。

②开始遍历原链表,当找到某节点的val比x更小,需要将其放入lessList中去,同时原链表和lessList链表都需要向后移动指针,greaterList同理。

③由于这是带头结点的链表,这里需要将lp(指向lessList的最后一个指针)指向greaterList的下一个节点(这里跳过头指针),然后释放greaterList(头指针),同理先保存lessList的头结点的next,然后释放头结点,最后得到的是一整串链表;

需要注意的是:由于每次gp指向的都是原链表的内容,如果最后一次指向的内容中next是新链表中的内容,那么就会导致循环链表的产生,所以当循环结束之后需要将gp的next置为空防止出现死循环

代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
typedef struct ListNode ListNode;
struct ListNode* partition(struct ListNode* head, int x) {// 定义两个带头结点的链表,分别保存比x大/小的节点ListNode* greaterList = (ListNode*)malloc(sizeof(ListNode));ListNode* lessList = (ListNode*)malloc(sizeof(ListNode));ListNode* gp = greaterList;ListNode* lp = lessList;// 遍历原链表while (head) {if (head->val < x) {lp->next = head;lp = lp->next;head = head->next;} else {gp->next = head;gp = gp->next;head = head->next;}}// 关键修正:确保greaterList的尾部正确终止(避免循环引用)gp->next = NULL;// 合并链表lp->next = greaterList->next;free(greaterList);head = lessList->next;free(lessList);return head;
}

 

http://www.dtcms.com/a/316421.html

相关文章:

  • 基于ARM+FPGA光栅数据采集卡设计
  • OpenCV学习 day5
  • 从「同步」到「异步」:用 aiohttp 把 Python 网络 I/O 榨到极致
  • Python--OCR(2)
  • 微算法科技(NASDAQ:MLGO)基于量子重加密技术构建区块链数据共享解决方案
  • 算法438. 找到字符串中所有字母异位词
  • 算法第31天|动态规划:最后一块石头的重量Ⅱ、目标和、一和零
  • 二分查找
  • 算法训练营day41 动态规划⑧ 121. 122.123.买卖股票的最佳时机1.2.3
  • 常用技术资料链接
  • Spring小细节
  • oelove奥壹新版v11.7旗舰版婚恋系统微信原生小程序源码上架容易遇到的几个坑,避免遗漏参数白屏显示等问题
  • Electron-updater + Electron-builder + IIS + NSIS + Blockmap 完整增量更新方案
  • 物联网后端系统架构:从基础到AI驱动的未来 - 第十章:AI促进IOT领域发生革命式发展
  • WebRTC采集模块技术详解
  • 阿里云百炼平台创建智能体-上传文档
  • Mysql使用Canal服务同步数据->ElasticSearch
  • Linux-环境变量
  • Transformer的并行计算与长序列处理瓶颈
  • 视频转二维码在教育场景中的深度应用
  • QT跨线程阻塞调用方法总结
  • SpringMVC 6+源码分析(四)DispatcherServlet实例化流程 3--(HandlerAdapter初始化)
  • 【机器学习深度学习】 知识蒸馏
  • 2.4.9-2.5.1监控项目工作-控制质量-确认范围-结束项目或阶段
  • 三极管三种基本放大电路:共射、共集、共基放大电路
  • 后量子时代已至?中国量子加密技术突破与网络安全新基建
  • 无监督学习聚类方法——K-means 聚类及应用
  • CMAQ空气质量模式实践技术及案例分析应用;CMAQ空气质量模式配置、运行
  • Go语言实战案例:使用sync.Mutex实现资源加锁
  • 一次完整的 Docker 启动失败排错之旅:从 `start-limit` 到 `network not found