算法宝典1——Java版本(此系列持续更新,这篇文章有20道)(有题目的跳转链接)(此份宝典包含了链表、栈、队列、二叉树的算法题)(上)

简介: 算法宝典1——Java版本(此系列持续更新,这篇文章有20道)(有题目的跳转链接)(此份宝典包含了链表、栈、队列、二叉树的算法题)

一、链表的算法题(目前10道)

1. 移除链表元素(力扣;思路:前后指针)

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

思路:

代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if(head == null){
            return null;
        }
        ListNode prev = head;
        ListNode cur = head.next;
        while(cur != null){
            if(cur.val == val){
                prev.next = cur.next;
                cur = cur.next;
            }else{
                cur = cur.next;
                prev = prev.next;
            }
        }
        //最后处理第一个节点
        if(head.val == val){
            head = head.next;
        }
         return head;
    }
}

运行结果:

时间复杂度和空间复杂度:

(1)时间复杂度:O(N);(2)空间复杂度:O(1)

2. 反转一个单链表 (力扣;思路:头插法)

题目:给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

思路:

代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        //特殊情况一:如果链表为空,就不需要返回
        if(head == null){
            return null;
        }
        //特殊情况二:只有一个节点
        if(head.next == null){
            return head;
        }
        ListNode cur = head.next;//把第二个节点地址保存起来
        head.next = null;//到时第一个节点的next肯定为空,在这里就可以提前改了
        //进入循环,开始进行头插法
        while(cur != null){
            ListNode curNext = cur.next;//保存cur指向的节点
            cur.next = head;//将第一个节点的地址赋值给cur.next
            head = cur;
            cur = curNext;
        }
        return head;
    }
}

运行结果:

时间复杂度和空间复杂度:

(1)时间复杂度:O(N);空间复杂度:O(1)

3. 链表的中间结点(力扣;思路:快慢指针)

题目:思路给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。

思路:

代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode middleNode(ListNode head) {
                //特殊情况:如果只有一个结点
        if(head.next == null){
            return head;
        }
        ListNode quick = head;//快指针
        ListNode slow = head;//慢指针
        //有两种条件都成立表示快指针不走了,(1)quick为空 (2)quick.next为空
        while(quick != null && quick.next != null){
            quick = quick.next.next;
            slow = slow.next;
        }
        return slow;
    }
}

运行结果:

时间复杂度和空间复杂度:

(1)时间复杂度:O(N);(2)空间复杂度:O(1)

4. 链表中倒数第k个结点(牛客网;思路:①快慢指针、②倒数第几个与步数的关系)

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

思路:

代码:

思路一:

import java.util.*;
/*
public class ListNode {
    int val;
    ListNode next = null;
    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindKthToTail(ListNode head, int k) {
        //输入值是否正确的判断
        if(k <= 0 || head == null){
            return null;
        }
        ListNode quick = head;
        ListNode slow = head;
        //先让quick走k - 1步
        while (k - 1 != 0){
            quick = quick.next;
            if(quick == null){
                return null;
            }
            k--;
        }
        //快慢指针一起走
        while(quick.next != null){
            quick = quick.next;
            slow = slow.next;
        }
        return slow;
    }
}

思路二:

public ListNode findKthToTail2(int k) {
        //输入值是否正确的判断
        if(k <= 0 || head == null){
            return null;
        }
        int count = 0;
        ListNode cur = head;
        while(cur != null){
            count++;
            cur = cur.next;
        }
        int i = count - k;
        cur = head;
        if(i < 0){
            return null;
        }
        while(i > 0){
            cur = cur.next;
            i--;
        }
        return cur;
    }

运行结果:

时间复杂度和空间复杂度:

(1)时间复杂度:O(N);(2)空间复杂度:O(1)

5. 合并两个有序链表(力扣)

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

思路:

代码:

//合并两个升序链表,有问题
    public ListNode mergeTwoLists(ListNode list1, ListNode list2){
        //处理空表的情况
        if(list1 == null && list2 !=null){
            return list2;
        }else if(list1 != null && list2 == null){
            return list1;
        }else if(list1 == null&&list2 == null){
            return null;
        }
        //创建两个地址引用,指向两个链表的首元素结点
        ListNode cur1 = list1;
        ListNode cur2 = list2;
        ListNode cur3 = null;//始终指向新链表的终端结点
        //创建一个newList引用
        ListNode newList = null;
        if (cur1.val <= cur2.val){
            newList = cur1;
            cur1 = cur1.next;
            cur3 = newList;
        }else if(cur1.val >= cur2.val){
            newList = cur2;
            cur2 = cur2.next;
            cur3 = newList;
        }
        //开始比较
        while(cur1 != null && cur2 != null ){
            if(cur1.val <= cur2.val){
                cur3.next = cur1;
                cur3 = cur3.next;
                cur1 = cur1.next;
            }else if(cur1.val > cur2.val){
                cur3.next = cur2;
                cur3 = cur3.next;
                cur2 = cur2.next;
            }
        }
        //当cur1 == null或cur2 == null时,意味着已经到链表的结尾,此时还需要将没有连接上的链表(cur1或cur2)连接接上
        cur3.next = null;
        if(cur1 != null){
            cur3.next = cur1;
        }
        if(cur2 != null){
            cur3.next = cur2;
        }
        return newList;
    }

运行结果:

6. 链表分割(牛客网)

题目:现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。

思路:

代码:

/*
public class ListNode {
    int val;
    ListNode next = null;
    ListNode(int val) {
        this.val = val;
    }
}*/
public class Partition {
    public ListNode partition(ListNode pHead, int x) {
                // write code here
        //把整个链表看成两部分,创建四个引用来分别指向这两部分
        ListNode bs = null;//小于x部分的起始引用
        ListNode be = null;//小于x部分的终端引用
        ListNode as = null;//大于或等于x部分的起始引用
        ListNode ae = null;//大于或等于x部分的终端引用
        ListNode cur = pHead;
        //开始循环
        while(cur != null){
            //有两种可能性,小于x或者大于等于x
            if(cur.val < x){
                //这部分也有两种情况,①当bs和be都为空,②bs不会空
                if(bs == null){
                    bs = cur;
                    be = cur;
                }else{
                    be.next = cur;
                    be = be.next;
                }
            }else{
                if(as == null){
                    as = cur;
                    ae = cur;
                }else{
                    ae.next = cur;
                    ae = ae.next;
                }
            }
            cur = cur.next;
        }
        //有可能不会同时存在小于x和大于等于x的数据
        if(bs == null){
            return as;
        }
        //特殊情况:如果第一段不为空
        be.next = as;
        //如果第二段为空
        if(as != null){
            ae.next = null;
        }
        return bs;
    }
}

运行结果:

7. 相交链表(力扣)

题目:给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

思路:

代码:

public MySinglelist.ListNode getIntersectionNode(MySinglelist.ListNode headA, MySinglelist.ListNode headB) {
        int lenA = 0;//用来记录长度
        int lenB = 0;
        MySinglelist.ListNode pl = headA;
        MySinglelist.ListNode ps = headB;
        //计算pl和ps的链表长度
        while(pl != null){
            lenA++;
            pl = pl.next;
        }
        while (ps != null){
            lenB++;
            ps = ps.next;
        }
        //1. len一定是一个正数  2.pl指向的链表一定是最长的  ps 指向的链表一定是最短的
        pl = headA;
        ps = headB;
        //计算彼此之间的差值
        int len = lenA - lenB;
        if(len < 0){
            pl = headB;
            ps = headA;
            len = lenB - lenA;
        }
        //先让长度多的链表先走len步
        while(len != 0){
            pl = pl.next;
            len--;
        }
        //开始找相交的节点
        while(pl != ps){
            ps = ps.next;
            pl = pl.next;
        }
        //pl == ps
       /* if(pl == null) {
            return null;
        }*/
        return pl;
    }

运行结果:

8.  链表的回文结构(牛客网)

题目:对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。

思路:

定义快慢指针:

(1)找到中间结点

(2)翻转慢指针到快指针部分的结点

(3)首尾互相判断是否符合回文结构

代码:

public boolean chkPalindrome(ListNode head) {
        if(head == null){
            return false;
        }
        if(head.next == null){
            return true;
        }
        // write code here
        //先定义快慢指针
        ListNode quick = head;
        ListNode slow = head;
        //1. 找中间结点
        while(quick != null && quick.next != null){
            quick = quick.next.next;
            slow = slow.next;
        }
        //2. 翻转
        ListNode cur = slow.next;
        while(cur != null){
            ListNode curNext = cur.next;//先记录下来后一个结点
            cur.next = slow;//实现翻转
            slow = cur;//保存上一个结点的地址
            cur = curNext;//找到下一个结点
        }
        //3.开始判断
        cur = head;
        while(cur != slow){
            //如果不是不相等,返回false
            if(cur.val != slow.val){
                return false;
            }
            //判断偶数的情况:
            if(cur.next == slow){
                return true;
            }
            cur = cur.next;
            slow = slow.next;
        }
        return true;
    }

运行结果:

9.  环形链表(力扣;思路:快慢指针、数学追击问题)

题目:给你一个链表的头节点 head ,判断链表中是否有环。

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

如果链表中存在环 ,则返回 true 。 否则,返回 false

思路:

代码:

//判断链表是否有环
    //第一种写法
    public boolean hasCycle() {
        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;
    }
    //第二种写法
    public boolean hasCycle2() {
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                break;
            }
        }
        if(fast  == null || fast.next == null){
            return false;
        }
        return true;
    }

运行结果:

相关文章
|
22天前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
58 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
22天前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
45 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
25天前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
25 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
6天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
6天前
|
Java Linux Windows
如何查看已安装的 Java 版本
要查看已安装的 Java 版本,打开命令提示符或终端,输入 `java -version`,回车后即可显示当前系统中 Java 的版本信息。
|
6天前
|
Ubuntu Java Linux
如何检查 Java 版本是否兼容
要检查Java版本是否兼容,可在命令行输入“java -version”查看当前安装的Java版本,然后对比目标应用所需的Java版本,确保其满足要求。
|
25天前
|
算法
数据结构与算法二:栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式
这篇文章讲解了栈的基本概念及其应用,并详细介绍了中缀表达式转换为后缀表达式的算法和实现步骤。
41 3
|
26天前
|
存储 安全 Java
【用Java学习数据结构系列】探索栈和队列的无尽秘密
【用Java学习数据结构系列】探索栈和队列的无尽秘密
28 2
|
28天前
|
算法 Java 数据中心
探讨面试常见问题雪花算法、时钟回拨问题,java中优雅的实现方式
【10月更文挑战第2天】在大数据量系统中,分布式ID生成是一个关键问题。为了保证在分布式环境下生成的ID唯一、有序且高效,业界提出了多种解决方案,其中雪花算法(Snowflake Algorithm)是一种广泛应用的分布式ID生成算法。本文将详细介绍雪花算法的原理、实现及其处理时钟回拨问题的方法,并提供Java代码示例。
59 2
|
20天前
|
Java Maven Spring
查看springboot版本支持最高的java版本
截至最近更新,Spring Boot 3.0及以上版本支持的最高Java版本为Java 17。鉴于技术的不断演进,建议直接参考Spring Boot的官方文档获取最准确的支持信息,因为这些版本兼容性可能会随着新版本的发布而有所变化。选择与你的Spring Boot版本相匹配的Java版本,可以确保充分利用框架特性,同时保证项目的稳定性和前瞻性。
34 0