顺序表和链表面试题

文章目录

  • 顺序表
    • (1)原地移除数组中所有的元素val,要求时间复杂度为O(N),空间复杂度为O(1)。
    • (2)删除有序数组中的重复项
    • (3)合并两个有序数组
  • 链表
    • (1)删除链表中等于给定值 val 的所有节点
    • (2)反转一个单链表
    • (3) 合并两个有序链表
    • (4)链表的中间结点
    • (5)链表中倒数第k个结点
    • (6)分割链表
    • (7)输入两个链表,找出它们的第一个公共结点(Y型)
    • (8)给你一个链表的头节点 head ,判断链表中是否有环。
    • (9)给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
    • (10)判定链表是否是回文

顺序表

(1)原地移除数组中所有的元素val,要求时间复杂度为O(N),空间复杂度为O(1)。

oj链接
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

双指针

class Solution {
    public int removeElement(int[] nums, int val) {
        int left = 0;
        for (int right = 0; right < nums.length; right++) {
            if (nums[right] != val) {
                nums[left++] = nums[right];
            }
        }
        return left;
    }
}

(2)删除有序数组中的重复项

oj链接
给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
返回 k

双指针

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums == null || nums.length == 0) return 0;
            int front = 0;
            int next = 1;
        while(next < nums.length){
            if(nums[front] != nums[next]){
                nums[++front] = nums[next];
            }
            next++;
        }
        return front+1;
    }
}

优化(相邻的元素不同不进行多余的赋值操作)
像[1,2,3,4,5]就不用进行赋值操作

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums == null || nums.length == 0) return 0;
        int front = 0;
        int next = 1;
        while(next < nums.length){
            if(nums[front] != nums[next]){
                if(next - front > 1){
                    nums[front + 1] = nums[next];
                }
                front++;
            }
            next++;
        }
        return front + 1;
    }
}

(3)合并两个有序数组

oj链接
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

从后面开始确定

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        while(m>0&&n>0) {
            if(nums1[m-1]>nums2[n-1]) {
                nums1[m+n-1] = nums1[m-1];
                m--;
            } else {
                nums1[m+n-1] = nums2[n-1];
                n--;
            }
        }
         while(n>0) {
            nums1[n-1] = nums2[n-1];
            n--;
        }
    }
}

链表

(1)删除链表中等于给定值 val 的所有节点

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

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        while(head!=null&&head.val==val) {
            head= head.next;
        }
        if(head == null) {
            return null;
        }
        ListNode prev = head;
        while(prev.next!=null) {
            if(prev.next.val==val) {
                prev.next=prev.next.next;
            } else {
                prev=prev.next;
            }
        }
        return head;
    }
}

(2)反转一个单链表

oj链接
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null)  return null;
        ListNode prev = null;
        ListNode cur = head;
        ListNode next = head.next;
        while(next != null) {
            cur.next = prev;
            prev = cur;
            cur = next;
            next = next.next;
        }
        cur.next = prev;
        return cur;
    }
}

递归

class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode newHead = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return newHead;
    }
}

(3) 合并两个有序链表

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

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode prehead = new ListNode(-1);
        ListNode prev = prehead;
        while (l1 != null && l2 != null) {
            if (l1.val <= l2.val) {
                prev.next = l1;
                l1 = l1.next;
            } else {
                prev.next = l2;
                l2 = l2.next;
            }
            prev = prev.next;
        }
        prev.next = l1 == null ? l2 : l1;
        return prehead.next;
    }
}

递归

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        }
        else if (l2 == null) {
            return l1;
        }
        else if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        }
        else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}

(4)链表的中间结点

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

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

快慢指针

慢指针走一步,快指针走两步

while循环条件不能换顺序

class Solution {
    public ListNode middleNode(ListNode head) {
            ListNode fast = head;
            ListNode slow = head;
            while(fast!=null&&fast.next!=null) {
                fast = fast.next.next;
                slow = slow.next;
            }
            return slow;
    }
}

(5)链表中倒数第k个结点

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

public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        ListNode fast = head;
        ListNode slow = head;
        if(k<=0||head==null) return null;
        while(k-1>0) {
            if(fast.next == null) return null;
            fast = fast.next;
            k--;
        }
        while(fast.next!= null) {
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}

(6)分割链表

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

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

class Solution {
    public ListNode partition(ListNode head, int x) {
        if(head==null) return null;
        ListNode less = new ListNode(-1);
        ListNode lessHead = less;
        ListNode larger = new ListNode(-1);
        ListNode largerHead = larger;
        while(head != null) {
            if(head.val<x) {
                less.next = head;
                less = less.next;
            } else {
                larger.next = head;
                larger = larger.next;
            }
            head = head.next;
        }
        larger.next = null;
        less.next = largerHead.next;
        return lessHead.next;
    }
}

方法二:

class Solution {
    public ListNode partition(ListNode head, int x) {
         ListNode cur = head;
        ListNode bs = null;
        ListNode be = null;
        ListNode as = null;
        ListNode ae = null;
        //使用cur来遍历 所有的节点
        while (cur != null) {
            if (cur.val < x) {
                if (bs == null) {
                    bs = cur;
                    be = cur;
                } else {
                    be.next = cur;
                    be = be.next;
                }
            } else {
                // >= x
                if (as == null) {
                    as = cur;
                    ae = cur;
                } else {
                    ae.next = cur;
                    ae = ae.next;
                }
            }
            cur = cur.next;
        }
        if (bs == null) {
            return as;
        }
        be.next = as;
        if (as != null) {
            ae.next = null;
        }
        return bs;
    }
}

如果bs为null,那么原链表最后一个节点(next是null)是ae,ae的next就不用置为null

(7)输入两个链表,找出它们的第一个公共结点(Y型)

oj链接
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

图示两个链表在节点 c1 开始相交:
在这里插入图片描述

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) {
            return null;
        }
        ListNode pA = headA, pB = headB;
        while (pA != pB) {
            pA = pA == null ? headB : pA.next;
            pB = pB == null ? headA : pB.next;
        }
        return pA;
    }
}

(8)给你一个链表的头节点 head ,判断链表中是否有环。

oj链接
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。
思路
快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针从链表起始位置开始运行,如果链表带环则一定会在环中相遇,否则快指针走到链表的末尾。

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

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

public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null &&  fast.next !=null) {
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow) {
                return true;
            }
        }
        return false;
    }
}

(9)给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

oj链接
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。
思路
设链表头部走x步到环的入口, 环有 y 个节点(包括环入口节点)
先跟上一题一样, fast走两步,slow走一步,如果有环的话在环中相遇.
设在环中走了s步(不包括环的入口节点)
那么slow走了x+s步
fast走了x+s+ny(n是圈数)
因为fast走的距离是slow的两倍
所以2(x+s) = x+s+n
y
即x=(n-1)*y+(y-s)
让fast从起始位置走,slow从相遇的位置走(n-1)*y+y-s步,他们会在环的入口相遇.

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null &&  fast.next !=null) {
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow) {
               fast = head;
               while(fast != slow) {
                   fast = fast.next;
                   slow = slow.next;
               }
               return fast;
            }
        }
        return null;
    }
}

(10)判定链表是否是回文

即正读和倒读一样的链表
牛客网链接
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。

给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。
思路
先用快慢指针找到中间节点(奇数中间,偶数第二个中间结点),将中间节点之后的节点反转,一头从头结点,一头从末尾结点开始比较.

奇数的情况

当两个指针相遇的时候返回true

偶数的情况

当第一个节点的下一个节点是第二个节点且相等就返回true

public class PalindromeList {
    public boolean chkPalindrome(ListNode A) {
        if(A==null) {
            return true;
        }
        ListNode fast = A;
        ListNode slow = A;
        while(fast!=null&&fast.next!=null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        ListNode prev = slow;
        ListNode cur =slow.next;
        while(cur!=null) {
            ListNode next = cur.next;
            cur.next = prev;
            prev = cur;
            cur = next;
        }
        while(A!=slow) {
            if(A.val!=prev.val) {
                return false;
            }
            //偶数的情况
            if(A.next==prev) {
                return true;
            }
            A=A.next;
            prev=prev.next;
        }
        return true;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/220201.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【PduR路由】PduR模块详细介绍

目录 1.PDUR模块功能介绍 2.关键概念理解 3.功能详细设计 3.1I-PDU handling 3.1.1 I-PDU Reception to upper module(s) 3.1.1.1 Communication Interface 3.1.1.2 Transport Protocol 3.1.2 I-PDU Transmission from upper module(s) 3.1.2.1 Multicast 3.1.2.2 Co…

Object Detection in 20 Years: A Survey(2019.5)

文章目录 Abstract1. Introduction1.1. Difference from other related reviews1.2. Difficulties and Challenges in Object Detection 2. OBJECT DETECTION IN 20 YEARS2.1. 目标检测路线图2.1.1. 里程碑:传统探测器&#xff08;粗略了解&#xff09;2.1.2. 里程碑:基于CNN的…

网工内推 | 上市公司初级网工,HCIP认证优先,14薪,享企业年金

01 易佰网络 招聘岗位&#xff1a;初级网络工程师 职责描述&#xff1a; 1.电脑周边设备&#xff08;打印机、扫描仪、传真机、复印机、投影仪等&#xff09;安装与维护&#xff1b; 2.局域网维护&#xff1b;无线网WLAN维护&#xff1b;监控系统维护&#xff1b; 3.固资维护管…

Lombok的踩坑系列之@Builder

背景&#xff1a; Lombok 这个插件大家日常工作中几乎是必备的&#xff0c;几个简单的注解就可以帮助我们减少一大坨get/set方法等&#xff1b;其中Builder注解使用的也很广泛&#xff0c;使用了建造者模式帮助我们构建出个性化的对象&#xff0c;本次踩坑点就在这个地方。 先…

《opencv实用探索·十》opencv双边滤波的简单理解

1、引言 OpenCV中的双边滤波&#xff08;Bilateral Filtering&#xff09;是一种保持边缘清晰的滤波方法&#xff0c;它考虑像素的空间关系和像素值之间的差异。双边滤波对于去除噪声的同时保持图像的边缘非常有效&#xff0c;它也是一种非线性滤波。 双边滤波采用了两个高斯滤…

孙宇晨入选The Giving Block年度加密慈善人物

日前,全球主流加密捐赠平台The Giving Block公布了2023年度“加密慈善人物”榜单,以表彰过去一年在加密慈善领域做出卓越贡献的个人。其中,波场TRON创始人、火币HTX全球顾问委员会委员孙宇晨因其瞩目的慈善成就入选该榜单。在榜单评语中,The Giving Block指出,孙宇晨做出了加密…

Verilog if语句阻断z状态传播

一、测试代码 设置两组输入输出&#xff0c;对比使用assign赋值语句与always if语句。 if_assign_test.v timescale 1ns / 1ps // // Engineer: wkk // Module Name: if_assign_test // module if_assign_test(input if_a_in,input if_b_in,input if_c_in,input if_d_in…

Servlet作业1

1.【单选题】 (10分) &#xff08;B &#xff09;是一个用 Java 编写的程序&#xff0c;是一种实现了Servlet接口的类&#xff0c;它是由web容器负责创建并调用&#xff0c;在服务器容器上运行&#xff0c;用于接收和响应用户的请求。 A.Filter B.Servlet C.Request D.Res…

c题目16:写一个递归函数,计算N阶乘

每日小语 一生中&#xff0c;最光辉的一天并非功成名就的那一天&#xff0c;而是从悲叹与绝望中产生对人生挑战与勇敢迈向意志的那一天。——福楼拜 自己思考 这个小语呢&#xff0c;我目前还达不到&#xff0c;只是顺其自然&#xff0c;很多东西做起来很有动力&#xff0c;…

MongoDB的学习记录

目录 MongoDB是什么MongoDB为什么存在MongoDB怎么用MongoDB解决什么MongoDB指令开发使用的mongoose模块讲解请看这篇文章 MongoDB是什么 MongoDB是一种开源的、面向文档的NoSQL数据库管理系统。它使用JSON格式来存储数据&#xff0c;具有高可扩展性、高性能、高可用性等特点&a…

探索Selenium的规避检测策略

Selenium之规避检测 背景 ​ 目前很多大网站有对selenium采取了监测机制。在正常情况下我们用浏览器访问相关网站的window.navigator.webdriver的值为 undefined或者为false。而使用selenium访问则该值为true。我们如何伪装&#xff0c;防止被检测出来呢&#xff1f; ​ 这是…

使用 PyWebCopy 在 Python 中克隆网页

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com PyWebCopy 是一个用于克隆网页内容的 Python 库&#xff0c;它允许用户从指定的 URL 复制整个网页并保存到本地。本文将介绍 PyWebCopy 的基本用法&#xff0c;以及如何克隆网页并保存网页内容到本地文件夹。 安…

Linux进程间通信之共享内存

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;Linux &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 本博客主要内容讲解共享内存原理和相关接口的介绍&#xff0c;以及一个…

服务端测试开发必备技能:Mock测试

什么是mock测试 Mock 测试就是在测试活动中&#xff0c;对于某些不容易构造或者不容易获取的数据/场景&#xff0c;用一个Mock对象来创建以便测试的测试方法。 Mock测试常见场景 无法控制第三方系统接口的返回&#xff0c;返回的数据不满足要求依赖的接口还未开发完成&#…

[Redis]基础入门

Redis入门 一、初识Redis Redis是一种键值型的NoSql数据库。 其中键值型&#xff0c;是指Redis中存储的数据都是以key、value对的形式存储&#xff0c;而value的形式多种多样&#xff0c;可以是字符串、数值&#xff0c;甚至是json。 NoSql则是相对于传统关系型数据库而言&a…

【安卓12源码】WMS系列:addWindow 和 removeWindow流程

一、Window 的属性 Window的属性定义在WindowManager的内部类LayoutParams中&#xff0c;了解Window的属性能够更好的理解WMS的内部原理。Window的属性有很多种&#xff0c;与应用开发最密切的有三种&#xff0c;它们分别是Type(Window的类型)、Flag(Window的标志)和SoftInputM…

luceda ipkiss教程 39:修改版图text字体的粗细

通过修改font的line_width就可以修改版图中text字体的粗细&#xff1a; 代码如下&#xff1a; from si_fab import all as pdk import ipkiss3.all as i3class demo(i3.PCell):class Layout(i3.LayoutView):def _generate_elements(self, elems):elems i3.PolygonText(layer…

处理k8s中创建ingress失败

创建ingress&#xff1a; 如果在创建过程中出错了&#xff1a; 处理方法就是&#xff1a; kubectl get ValidatingWebhookConfiguration kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission 然后再次创建&#xff0c;发现可以&#xff1a;

AI封测需求强劲, AMD、英伟达等巨头将助推产业链增长 | 百能云芯

近期&#xff0c;超微&#xff08;AMD&#xff09;和英伟达&#xff08;NVIDIA&#xff09;相继发布了新一轮AI芯片&#xff0c;为封测产业链注入了新的活力。据业内人士透露&#xff0c;客户端对AI封测的需求愈发强劲&#xff0c;整体量能超过原先的估计&#xff0c;其中日月光…

【yolov8】与yolov5的区别及改进详解

图像识别技术在物联网、智能监控等领域广泛应用。而深度学习中的目标检测技术&#xff0c;能够帮助我们对图像中的目标进行识别&#xff0c;进而实现自动化控制。目前&#xff0c;Yolov8和Yolov5是目标检测领域热门的模型。 yolo目标检测原理yolov5详解yolov8yolov8结构图Conv模…