程序员代码面试指南(左程云著)java学习笔记

目录

常见问题总结

  1. 读取输入数据两种方法 (BufferedReader比较快,不容易出现报错)

    1Scanner  s = new Scanner(System.in);  
    	int i = s.nextInt();  
    2BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    	String[] inputDate = br.readLine().trim().split(" ");
    	int i = Integer.parseInt(inputDate[0]);
    
  2. 有一题while(fast.next.next != null && fast.next != null)出现了空指针异常的错误。正确应改为while(fast.next != null && fast.next.next != null)

第一章 栈和队列

CD5 设计getMin功能的栈

描述

实现一个特殊功能的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。

输入描述:

第一行输入一个整数N,表示对栈进行的操作总数。

下面N行每行输入一个字符串S,表示操作的种类。

如果S为"push",则后面还有一个整数X表示向栈里压入整数X。

如果S为"pop",则表示弹出栈顶操作。

如果S为"getMin",则表示询问当前栈中的最小元素是多少。

输出描述:

对于每个getMin操作,输出一行表示当前栈中的最小元素是多少。

示例1

输入:

6
push 3
push 2
push 1
getMin
pop
getMin

输出:

1
2

备注:

1<=N<=1000000

-1000000<=X<=1000000

数据保证没有不合法的操作

解答

import java.util.Scanner;
import java.util.Stack;

public class Main
{
    public static class MyStack1
    {
        private Stack<Integer> stackData;
        private Stack<Integer> stackMin;

        public MyStack1()
        {
            this.stackData = new Stack<Integer>();
            this.stackMin = new Stack<Integer>();
        }

        public void push(int newNum)
        {
            if (this.stackMin.isEmpty())
            {
                this.stackMin.push(newNum);
            }
            else if (newNum <= this.getMin())
            {
                this.stackMin.push(newNum);
            }
            this.stackData.push(newNum);
        }

        public int getMin()
        {
            if (this.stackMin.isEmpty())
            {
                throw new RuntimeException("Your stack is empty.");
            }
            return this.stackMin.peek();
        }

        public int pop()
        {
            if (this.stackData.isEmpty())
            {
                throw new RuntimeException("Your stack is empty.");
            }
            int value = this.stackData.pop();
            if (value == this.getMin())
            {
                this.stackMin.pop();
            }
            return value;
        }

    }

    public static class MyStack2
    {
        private Stack<Integer> stackData;
        private Stack<Integer> stackMin;

        public MyStack2()
        {
            this.stackData = new Stack<Integer>();
            this.stackMin = new Stack<Integer>();
        }
        public void push(int newNum)
        {
            if (stackMin.isEmpty())
            {
                this.stackMin.push(newNum);
            }
            else if (newNum <= this.getMin())
            {
                this.stackMin.push(newNum);
            }
            else
            {
                int newMin = this.stackMin.peek();
                this.stackMin.push(newMin);
            }
            this.stackData.push(newNum);
        }

        public int getMin()
        {
            if (this.stackData.isEmpty())
            {
                throw new RuntimeException("Your stack is empty");
            }
            return this.stackMin.peek();
        }

        public int pop()
        {
            if (this.stackData.isEmpty())
            {
                throw new RuntimeException("Your stack is empty");
            }
            this.stackMin.pop();
            return this.stackData.pop();
        }
    }
    public static void main(String[] args)
    {
        /*
        MyStack1 myStack1 = new MyStack1();
        Scanner scanner = new Scanner(System.in);
        int n = Integer.parseInt(scanner.nextLine());
        for (int i = 1; i <= n; i++)
        {
            String[] inputData = scanner.nextLine().split(" ");
            if (inputData[0].equals("push"))
            {
                myStack1.push(Integer.parseInt(inputData[1]));
            }
            else if (inputData[0].equals("pop"))
            {
                myStack1.pop();
            }
            else if (inputData[0].equals("getMin"))
            {
                System.out.println(myStack1.getMin());
            }
        }
        scanner.close();
        */
        MyStack2 myStack2 = new MyStack2();
        Scanner scanner = new Scanner(System.in);
        int n = Integer.parseInt(scanner.nextLine());
        for (int i = 1; i <= n; i++)
        {
            String[] inputData = scanner.nextLine().split(" ");
            if (inputData[0].equals("push"))
            {
                myStack2.push(Integer.parseInt(inputData[1]));
            }
            else if (inputData[0].equals("pop"))
            {
                myStack2.pop();
            }
            else if (inputData[0].equals("getMin"))
            {
                System.out.println(myStack2.getMin());
            }
        }
        scanner.close();

    }
}

在这里插入图片描述

CD6 由两个栈组成的队列

描述

用两个栈实现队列,支持队列的基本操作。

输入描述:

第一行输入一个整数N,表示对队列进行的操作总数。

下面N行每行输入一个字符串S,表示操作的种类。

如果S为"add",则后面还有一个整数X表示向队列尾部加入整数X。

如果S为"poll",则表示弹出队列头部操作。

如果S为"peek",则表示询问当前队列中头部元素是多少。

输出描述:

对于每一个为"peek"的操作,输出一行表示当前队列中头部元素是多少。

示例1

输入:

6
add 1
add 2
add 3
peek
poll
peek

输出:

1
2

备注:

1<=N<=1000000

-1000000<=X<=1000000

数据保证没有不合法的操作

解答

import java.util.Scanner;
import java.util.Stack;

public class Main
{
    public static class TwoStacksQueue
    {
        // 定义两个栈StackPush\ StackPop
        public Stack<Integer> StackPush;
        public Stack<Integer> StackPop;

        // 构造函数

        public TwoStacksQueue()
        {
            this.StackPush = new Stack<Integer>();
            this.StackPop = new Stack<Integer>();
        }

        // pushToPop函数
        // 两个前提条件: stackPop非空才能倒入, stackPush必须倒完
        public void pushToPop()
        {
            if (this.StackPop.isEmpty())
            {
                while (!this.StackPush.isEmpty())
                {
                    this.StackPop.push(this.StackPush.pop());
                }
            }
        }
        // add函数
        public void add(int x)
        {
            this.StackPush.push(x);
            pushToPop();
        }
        // poll函数
        public int poll()
        {
            // 如果栈都为空则报错
            if (this.StackPush.isEmpty() && this.StackPop.isEmpty())
            {
                throw new RuntimeException("Your queue is empty");
            }
            pushToPop();
            return this.StackPop.pop();

        }
        // peek函数
        public int peek()
        {
            if (this.StackPush.isEmpty() && this.StackPop.isEmpty())
            {
                throw new RuntimeException("Your queue is empty");
            }
            pushToPop();
            return this.StackPop.peek();
        }
    }

    public static void main(String[] args) {
        TwoStacksQueue queue = new TwoStacksQueue();
        Scanner scanner = new Scanner(System.in);
        int n = Integer.parseInt(scanner.nextLine());
        for (int i = 1; i <= n; i++)
        {
            String[] inputData = scanner.nextLine().split(" ");
            if (inputData[0].equals("add"))
            {
                queue.add(Integer.parseInt(inputData[1]));
            }
            else if (inputData[0].equals("poll"))
            {
                queue.poll();
            }
            else if (inputData[0].equals("peek"))
            {
                System.out.println(queue.peek());
            }
        }

        scanner.close();
    }
}

在这里插入图片描述

CD7 用递归函数和栈逆序一个栈

描述

一个栈依次压入1,2,3,4,5,那么从栈顶到栈底分别为5,4,3,2,1。将这个栈转置后,从栈顶到栈底为1,2,3,4,5,也就是实现栈中元素的逆序,但是只能用递归函数来实现,不能用其他数据结构。

输入描述:

输入数据第一行一个整数N为栈中元素的个数。

接下来一行N个整数X_iX**i表示一个栈依次压入的每个元素。

输出描述:

输出一行表示栈中元素逆序后的栈顶到栈底的每个元素

示例1

输入:

5
1 2 3 4 5

输出:

1 2 3 4 5

解答

import java.util.Scanner;
import java.util.Stack;

public class Main
{
    //  递归函数一:将栈底的元素返回并移除
    public static int getAndRemoveLastElement(Stack<Integer> stack)
    {
        // 记录位置方便递归
        int x = stack.pop();
        if (stack.isEmpty())
        {
            return x;
        }
        else
        {
            int last = getAndRemoveLastElement(stack);
            stack.push(x); // 恢复原来顺序
            return last;
        }
    }
    public static void reverse(Stack<Integer> stack)
    {
        if (stack.isEmpty())
        {
            return ;
        }
        int x = getAndRemoveLastElement(stack);
        reverse(stack);
        stack.push(x);
    }

    
    public static void main(String[] args)
    {
        Stack<Integer> stack = new Stack<>();
        Scanner scanner = new Scanner(System.in);
        int n = Integer.parseInt(scanner.nextLine());
        for (int i = 1; i <= n; i++)
        {
            int x = scanner.nextInt();
            stack.push(x);
        }
        reverse(stack);
        while (!stack.isEmpty())
        {
            System.out.print(stack.pop() + " ");
        }
        System.out.println();
    }
}

在这里插入图片描述

CD13 用一个栈实现另一个栈的排序

描述

一个栈中元素的类型为整型,现在想将该栈从顶到底按从大到小的顺序排序,只许申请一个栈。除此之外,可以申请新的变量,但不能申请额外的数据结构。如何完成排序?

输入描述:

第一行输入一个N,表示栈中元素的个数

第二行输入N个整数a_ia**i表示栈顶到栈底的各个元素

输出描述:

输出一行表示排序后的栈中栈顶到栈底的各个元素。

示例1

输入:

5
5 8 4 3 6

输出:

8 6 5 4 3

备注:

1 <= N <= 10000
-1000000 <= a_nan <= 1000000

代码

import java.util.Scanner;
import java.util.Stack;

public class Main
{
    // 要排序的栈为stack, 辅助栈为help, 在stack栈进行pop操作,弹出的元素记作cur
    // 分两种情况讨论: 1、 cur <= help栈顶元素, 则将cur直接压入help栈中
    //                  2、 cur >  help栈顶元素   stack.push(help.pop())直到cur <= help栈顶元素位置, 最后把cur压入help中
    public static void sortStackByStack(Stack<Integer> stack)
    {
        Stack<Integer> help = new Stack<>();
        while (!stack.isEmpty())
        {
            int cur = stack.pop();
            while (!help.isEmpty() && cur > help.peek())
            {
                stack.push(help.pop());
            }
            help.push(cur);
        }

        while (!help.isEmpty())
        {
            stack.push(help.pop());
        }
    }

    public static void main(String[] args)
    {
        Scanner scanner = new Scanner(System.in);
        Stack<Integer> stack = new Stack<>();
        int n = Integer.valueOf(scanner.nextLine());
        // 读入待排序数据
        for (int i = 0; i < n; i++)
        {
            int x = scanner.nextInt();
            stack.push(x);
        }
        sortStackByStack(stack);
        // 输出Stack中排好序的结果
        while (!stack.isEmpty())
        {
            System.out.print(stack.pop() + " ");
        }
        System.out.println();
    }
}

在这里插入图片描述

CD15 生成窗口最大值数组

描述

有一个整型数组arr和一个大小为w的窗口从数组的最左边滑到最右边,窗口每次向右边滑一个位置,求每一种窗口状态下的最大值。(如果数组长度为n,窗口大小为w,则一共产生n-w+1个窗口的最大值)

输入描述:

第一行输入n和w,分别代表数组长度和窗口大小

第二行输入n个整数X_iX**i,表示数组中的各个元素

输出描述:

输出一个长度为n-w+1的数组res,res[i]表示每一种窗口状态下的最大值

示例1

输入:

8 3
4 3 5 4 3 3 6 7

输出:

5 5 5 4 6 7

说明:

例如,数组为[4,3,5,4,3,3,6,7],窗口大小为3时:

[4 3 5] 4 3 3 6 7        窗口中最大值为5

4 [3 5 4] 3 3 6 7        窗口中最大值为5

4 3 [5 4 3] 3 6 7        窗口中最大值为5

4 3 5 [4 3 3] 6 7        窗口中最大值为4

4 3 5 4 [3 3 6] 7        窗口中最大值为6

4 3 5 4 3 [3 6 7]        窗口中最大值为7

输出的结果为{5 5 5 4 6 7}

备注:

1\le w \le n \le 10000001≤w≤n≤1000000
-1000000 \le X_i \le 1000000−1000000≤Xi≤1000000

解答

import java.util.LinkedList;
import java.util.Scanner;

public class Main
{
    public static int[] getMaxWindow(int[] arr, int w)
    {
        // 检验参数是否有效
        if (arr == null && w < 1 && arr.length < w)
        {
            return null;
        }
        LinkedList<Integer> qmax = new LinkedList<>();
        int[] res = new int[arr.length - w + 1];
        int index = 0;
        for (int i = 0; i < arr.length; i++)
        {
            while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[i] )
            {
                qmax.pollLast();
            }
            qmax.addLast(i);
            // {0, 1,} 2, 3, 4... w = 2
            // 当 i = 2时, 0出队 也就是 i - w == qmax.peekFirst() 队首元素出队
            // qmax要溢出时, 队首元素出队
            if (qmax.peekFirst() == i - w)
            {
                qmax.pollFirst();
            }
            if (i >= w - 1)
            {
                // 至少检索了w个元素后,才能把队首元素下标对应值放入res数组中
                res[index++] = arr[qmax.peekFirst()];
            }
        }
        return res;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String[] inputData = scanner.nextLine().split(" ");
        int len = Integer.parseInt(inputData[0]);
        int w = Integer.parseInt(inputData[1]);
        int[] arr = new int[len];
        int[] res = new int[len - w + 1];
        int i = 0;
        for (i = 0; i < len; i++)
        {
            int x = scanner.nextInt();
            arr[i] = x;
        }
        res = getMaxWindow(arr, w);
        for (i = 0; i < res.length; i++)
        {
            System.out.print(res[i] + " ");
        }
        System.out.println();
    }
}

在这里插入图片描述

CD101 单调栈结构

描述

给定一个不含有重复值的数组 arr,找到每一个 i 位置左边和右边离 i 位置最近且值比 arr[i] 小的位置。返回所有位置相应的信息。

输入描述:

第一行输入一个数字 n,表示数组 arr 的长度。

以下一行输出 n个数字,表示数组的值。

输出描述:

输出n行,每行两个数字 L 和 R,如果不存在,则值为-1,下标从0开始。

示例1

输入:

7
3 4 1 5 6 2 7

复制

输出:

-1 2
0 2
-1 -1
2 5
3 5
2 -1
5 -1

复制

备注:

1 \le n \le 10000001≤n≤1000000
-1000000 \le arr_i \le 1000000−1000000≤arri≤1000000

解答

import java.util.Scanner;
import java.util.Stack;

public class Main
{
    public static int[][] rightWay(int[] arr)
    {
        int[][] res = new int[arr.length][2];
        for (int i = 0; i < arr.length; i++)
        {
            int leftLessIndex = -1;
            int rightLessIndex = -1;
            int cur = i - 1;
            while (cur >= 0)
            {
                if (arr[cur] < arr[i])
                {
                    leftLessIndex = cur;
                    break;
                }
                cur--;
            }

            cur = i + 1;
            while (cur < arr.length)
            {
                if (arr[cur] < arr[i])
                {
                    rightLessIndex = cur;
                    break;
                }
                cur++;
            }
            res[i][0] = leftLessIndex;
            res[i][1] = rightLessIndex;
        }
        return res;
    }

    public static int[][] getNearLessNoRepeat(int[] arr)
    {
        int[][] res = new int[arr.length][2];
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < arr.length; i++)
        {
            while (!stack.isEmpty() && arr[stack.peek()] > arr[i])
            {
                int index = stack.pop();
                int leftLessIndex = stack.isEmpty() ? -1 : stack.peek();
                int rightLessIndex = i;
                res[index][0] = leftLessIndex;
                res[index][1] = rightLessIndex;
            }
            stack.push(i);
        }
        while (!stack.isEmpty())
        {
            int popIndex = stack.pop();
            res[popIndex][1] = -1; // 结算阶段rightLessIndex为-1, 因为没有数使它们弹出
            res[popIndex][0] = stack.isEmpty() ? -1 : stack.peek();
            // 判断弹出的数下面有没有数, 如果没有rightLessIndex为-1; 如果有则为下面数的下标

        }
        return res;
    }

    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int n = Integer.parseInt(s.nextLine());
        int[] arr = new int[n];
        for (int i = 0; i < n; i++)
        {
            arr[i] = s.nextInt();
        }
        // int[][] res = rightWay(arr);
        int[][] res = getNearLessNoRepeat(arr);
        for (int i = 0; i < res.length; i++)
        {
            System.out.println(res[i][0] + " " + res[i][1]);
        }
    }
}

在这里插入图片描述

CD188 单调栈结构(进阶)

描述

给定一个可能含有重复值的数组 arr,找到每一个 i 位置左边和右边离 i 位置最近且值比 arr[i] 小的位置。返回所有位置相应的信息。

输入描述:

第一行输入一个数字 n,表示数组 arr 的长度。
以下一行输入 n 个数字,表示数组的值

输出描述:

输出n行,每行两个数字 L 和 R,如果不存在,则值为 -1,下标从 0 开始。

示例1

输入:

7
3 4 1 5 6 2 7

输出:

-1 2
0 2
-1 -1
2 5
3 5
2 -1
5 -1

备注:

1 \leq n \leq 10000001≤n≤1000000
-1000000 \leq arr_i \leq 1000000−1000000≤arri≤1000000

解答

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.Stack;

public class Main
{
    public static int[][] getNearLess(int[] arr)
    {
        int[][] res = new int[arr.length][2];
        Stack<List<Integer>> stack = new Stack<>();
        for (int i = 0; i < res.length; i++)
        {
            while (!stack.isEmpty() && arr[i] < arr[stack.peek().get(0)])
            {
                List<Integer> popList = stack.pop();
                // 取出位于下面的列表中,最晚加入的那个
                int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);
                for (Integer x : popList)
                {
                    res[x][0] = leftLessIndex;
                    res[x][1] = i;
                }
            }

            // 当前数比stack栈顶数相等
            if (!stack.isEmpty() && arr[stack.peek().get(0)] == arr[i])
            {
                stack.peek().add(i);
            }
            else
            {
                // 当前数比stack栈顶数大或者此时为空栈
                ArrayList<Integer> list = new ArrayList<>();
                list.add(i);
                stack.push(list);
            }
        }

        while (!stack.isEmpty())
        {
            List<Integer> popList = stack.pop();
            int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);
            for (Integer x : popList)
            {
                res[x][0] = leftLessIndex;
                res[x][1] = -1;
            }
        }
        return res;
    }
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int n = Integer.parseInt(s.nextLine());
        int[] arr = new int[n];
        String[] str = s.nextLine().split(" ");
        for (int i = 0; i < n; i++)
        {
            arr[i] = Integer.parseInt(str[i]);
        }
        int[][] res = getNearLess(arr);
        for (int i = 0; i < res.length; i++)
        {
            System.out.println(res[i][0] + " " + res[i][1]);
        }
    }
}

在这里插入图片描述

第二章 链表问题

CD48 打印两个升序链表的公共部分

描述

给定两个升序链表,打印两个升序链表的公共部分。

输入描述:

第一个链表的长度为 n。

第二个链表的长度为 m。

链表结点的值为 val。

输出描述:

输出一行整数表示两个升序链表的公共部分的值 (按升序输出)。

示例1

输入:

4
1 2 3 4
5
1 2 3 5 6

输出:

1 2 3

备注:

1 \le n,m\le 10000001≤n,m≤1000000
INTMIN \le val \le INTMAXINTMIN≤val≤INTMAX

解答

import java.util.Scanner;

public class Main
{
    public static class Node
    {
        public int value;
        public Node next;
        public Node(int v)
        {
            this.value = v;
        }
    }

    public static void printCommonPart(Node head1, Node head2)
    {
        while (head1 != null && head2 != null)
        {
            if (head1.value > head2.value)
            {
                head2 = head2.next;
            }
            else if (head1.value < head2.value)
            {
                head1 = head1.next;
            }
            else
            {
                System.out.print(head1.value + " ");
                head1 = head1.next;
                head2 = head2.next;
            }
        }
        System.out.println();

    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int m = 0, n = 0;
        m = Integer.parseInt(scanner.nextLine());
        Node head1 = new Node(scanner.nextInt());
        Node cur = head1;
        for (int i = 1; i < m; i++)
        {
            cur.next = new Node(scanner.nextInt());
            cur = cur.next;
        }
        scanner.nextLine();
        n = Integer.parseInt(scanner.nextLine());
        Node head2 = new Node(scanner.nextInt());
        cur = head2;
        for (int i = 1; i < n; i++)
        {
            cur.next = new Node(scanner.nextInt());
            cur = cur.next;
        }

        printCommonPart(head1, head2);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dqnppjTG-1642684708381)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20220119210416383.png)]

CD49 在链表中删除倒数第K个节点

描述

给出一个单链表,返回删除单链表的倒数第 K 个节点后的链表。

输入描述:

第一行输入两个正整数 n, K ,分别表示链表的长度和要删除单链表倒数第K个节点。 接下来一行有 n 个整数,依次表示单链表中的各个节点的节点值val。

输出描述:

在给定的函数内返回删除倒数第K个节点后的链表的头指针。

示例1

输入:

5 4
1 2 3 4 5

输出:

1 3 4 5

备注:

1 \le K \le n \le 10000001≤K≤n≤1000000
-1000000 \le val \le 1000000−1000000≤val≤1000000

解答

import java.util.Scanner;

public class Main
{
    public static class Node
    {
        public int value;
        public Node next;
        public Node(int value)
        {
            this.value = value;
        }
    }

    public static Node removeLastNode(Node head, int i)
    {
        if (head == null || i < 1)
        {
            return head;
        }

        Node cur = head;
        while (cur != null)
        {
            i--;
            cur = cur.next;
        }

        if (i > 0)
        {
            // i > 0 不用调整链表
        }
        else if (i == 0)
        {
            // 要删除头结点
            head = head.next;
        }
        else
        {
            cur = head;
            i++;
            while (i != 0)
            {
                i++;
                cur = cur.next;
            }
            cur.next = cur.next.next;
        }

        return head;
    }

    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int m = s.nextInt(), k = s.nextInt();
        s.nextLine();
        Node head1 = new Node(s.nextInt());
        Node cur = head1;
        for (int i = 1; i < m; i++)
        {
            cur.next = new Node(s.nextInt());
            cur = cur.next;
        }
        removeLastNode(head1, k);

        cur = head1;
        while (cur != null)
        {
            System.out.print(cur.value + " ");
            cur = cur.next;
        }
        System.out.println();
    }

}

在这里插入图片描述

CD106 删除链表的中间节点

描述

给定一个链表,实现删除链表第 K 个节点的函数。

输入描述:

n 表示链表的长度。

m 表示删除链表第几个节点。

val 表示链表节点的值。

输出描述:

在给定的函数中返回链表的头指针。

示例1

输入:

5 3
1 2 3 4 5

输出:

1 2 4 5

备注:

1 \le K \le n \le 10000001≤K≤n≤1000000
-1000000 \le val \le 1000000−1000000≤val≤1000000

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NlfKmD2M-1642684708385)(C:\Users\lenovo\Desktop\资源\IMG_20220119_224721.jpg)]

解答

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;

public class Main
{
    public static class Node
    {
        public int value;
        public Node next;
        public Node(int v)
        {
            this.value = v;
        }
    }
    /*
    public static Node removeMidNode(Node head)
    {
        if (head == null || head.next == null)
        {
            return head;
        }
        if (head.next.next == null)
        {
            return head.next;
        }
        Node slow = head;
        Node fast = head.next.next;
        // 使用快慢指针法 快指针一次走两步,慢指针一次走一步
        // fast , slow = head 初始化
        // fast->next == null || fast->next->next == null 时 slow 指针为要删除的那个
        // 因此 fast初始化要改成fast = head->next->next;
        while (fast.next != null && fast.next.next != null)
        {
            fast = fast.next.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;
        return head;
    }

    public static Node removeByRatio(Node head, int a, int b)
    {
        // 我们假设链表的节点总数为n
        // 我们要删除的节点 (double) a * (double) n / (double) b 然后再向上取整
        if (a < 1 || a > b)
        {
            // 分母比1小 或者 分数>1 为非法参数
            return null;
        }
        int n = 0;
        Node cur = head;
        while (cur != null)
        {
            n++;
            cur = cur.next;
        }
        n = (int) Math.ceil((double)(a * n) / (double) (b));
        if (n == 1)
        {
            head = head.next;
        }
        else if (n > 1)
        {
            cur = head;
            n--;
            while (n > 1)
            {
                cur = cur.next;
                n--;
            }
            cur.next = cur.next.next;
        }
        return head;
    }

    public static void main(String[] args) {
        // 实例输入
        // 5
        // 1 2 3 4 5
        //输出
        // 1 2 4 5
        Scanner s = new Scanner(System.in);
        int m = Integer.parseInt(s.nextLine());
        Node head = new Node(s.nextInt());
        Node cur = head;
        for (int i = 1; i < m; i++)
        {
            cur.next = new Node(s.nextInt());
            cur = cur.next;
        }
        // removeMidNode(head);

        // 链表为1->2->3->4->5
        // a = 1 b = 3 删除第二个节点
        // 输出 1->3->4->5
        s.nextLine();
        int a = s.nextInt(), b = s.nextInt();
        removeByRatio(head, a, b);


        cur = head;
        while (cur != null)
        {
            System.out.print(cur.value + " ");
            cur = cur.next;
        }
        System.out.println();



    }
    */
    public static Node removeKNode(Node head, int k)
    {
        if (head == null || k < 1)
        {
            return head;
        }
        if (k == 1)
        {
            return head.next;
        }
        Node cur = head;
        k--;
        while (k > 1)
        {
            cur = cur.next;
            k--;
        }
        cur.next = cur.next.next;
        return head;
    }
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(
                new InputStreamReader( System.in ) );
        String[] inputData = br.readLine().trim().split(" ");
        int n = Integer.parseInt(inputData[0]), k = Integer.parseInt(inputData[1]);
        inputData = br.readLine().trim().split(" ");
        Node head = new Node(Integer.parseInt(inputData[0]));
        Node cur = head;
        for (int i = 1; i < n; i++)
        {
            cur.next = new Node(Integer.parseInt(inputData[i]));
            cur = cur.next;
        }
        removeKNode(head, k);

        cur = head;
        while (cur != null)
        {
            System.out.print(cur.value + " ");
            cur = cur.next;
        }
        System.out.println();
    }
}

CD107 反转单向链表

描述

实现反转单向链表和双向链表的函数。

如 1->2->3 反转后变成 3->2->1。

输入描述:

第一行一个整数 n,表示单链表的长度。

第二行 n 个整数 val 表示单链表的各个节点。

第三行一个整数 m,表示双链表的长度。

第四行 m 个整数 val 表示双链表的各个节点。

输出描述:

在给定的函数内返回相应链表的头指针。

示例1

输入:

3
1 2 3
4
1 2 3 4

输出:

3 2 1
4 3 2 1

备注:

1 \le n,m\le10000001≤n,m≤1000000
-1000000 \le val \le 1000000−1000000≤val≤1000000

解答

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main
{
    public static class Node
    {
        public int value;
        public Node next;
        public Node(int value)
        {
            this.value = value;
        }
    }

    public static class DoubleNode
    {
        public int value;
        public DoubleNode last;
        public DoubleNode next;
        public DoubleNode(int v)
        {
            this.value = v;
        }
    }

    public static Node reverseList(Node head)
    {
        Node pre = null;
        Node next = null;
        while (head != null)
        {
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }
    public static DoubleNode reverseDoubleNodeList(DoubleNode head)
    {
        DoubleNode pre = null;
        DoubleNode next = null;
        while (head != null)
        {
            next = head.next;
            head.last = next;
            head.next = pre; // 通过这两步head节点完成了反转
            pre = head;
            head = next;
        }
        return pre; // 注意输出是pre不是head
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] inputData = br.readLine().trim().split(" ");
        int m = Integer.parseInt(inputData[0]);
        inputData = br.readLine().trim().split(" ");
        Node head1 = new Node(Integer.parseInt(inputData[0]));
        Node cur = head1;
        for (int i = 1; i < m; i++)
        {
            cur.next = new Node(Integer.parseInt(inputData[i]));
            cur = cur.next;
        }

        inputData = br.readLine().trim().split(" ");
        int n = Integer.parseInt(inputData[0]);
        inputData = br.readLine().trim().split(" ");
        DoubleNode head2 = new DoubleNode(Integer.parseInt(inputData[0]));
        DoubleNode pre = null, current = head2, next = null;
        for (int i = 1; i < n; i++)
        {
            current.next = new DoubleNode(Integer.parseInt(inputData[i]));
            current.last = pre;
            pre = current;
            current = current.next;
            next = current.next;
        }
        head1 = reverseList(head1);
        head2 = reverseDoubleNodeList(head2);

        cur = head1;
        while (cur != null)
        {
            System.out.print(cur.value + " ");
            cur = cur.next;
        }
        System.out.println();

        current = head2;
        while (current != null)
        {
            System.out.print(current.value + " ");
            current = current.next;
        }
        System.out.println();
    }
}

在这里插入图片描述

CD108 反转部分单向链表

描述

给定一个单链表,在链表中把第 L 个节点到第 R 个节点这一部分进行反转。

输入描述:

n 表示单链表的长度。

val 表示单链表各个节点的值。

L 表示翻转区间的左端点。

R 表示翻转区间的右端点。

输出描述:

在给定的函数中返回指定链表的头指针。

示例1

输入:

5
1 2 3 4 5
1 3

输出:

3 2 1 4 5

备注:

1 \le n \le 10000001≤n≤1000000
1 \leq L \leq R \leq n1≤L≤R≤n
-1000000 \le val \le 1000000−1000000≤val≤1000000

解答

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main
{
    public static class Node
    {
        public int value;
        public Node next;
        public Node(int value)
        {
            this.value = value;
        }
    }

    public static Node reversePart(Node head, int from, int to)
    {
        int len = 0;
        Node cur = head;
        Node fromPre = null, toNext = null;

        while (cur != null)
        {
            len++;
            fromPre = (len == from - 1) ? cur : fromPre; // 找到反转部分的头结点前面一个节点
            toNext  = (len == to   + 1) ? cur : toNext;  // 找到反转部分的头结点后面一个节点
            cur = cur.next;
        }
        if (from > to || from < 1 || to > len)
        {
            // 不满足题目 1<=from<=to<=N的条件,返回头结点
            return head;
        }
        Node node1 = (fromPre == null) ? head : fromPre.next;
        Node node2 = node1.next;
        node1.next = toNext;
        Node next = null;
        while (node2 != toNext)
        {
            next = node2.next;
            node2.next = node1;
            node1 = node2;
            node2 = next;
        }

        if (fromPre != null)
        {
            fromPre.next = node1;
            return head;
        }
        else
        {
            return node1;
        }


    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] inputData = br.readLine().trim().split(" ");
        int m = Integer.parseInt(inputData[0]);
        inputData = br.readLine().trim().split(" ");
        Node head = new Node(Integer.parseInt(inputData[0]));
        Node cur = head;
        for (int i = 1; i < m; i++)
        {
            cur.next = new Node(Integer.parseInt(inputData[i]));
            cur = cur.next;
        }

        inputData = br.readLine().trim().split(" ");
        int from = Integer.parseInt(inputData[0]), to = Integer.parseInt(inputData[1]);

        head = reversePart(head, from, to);

        cur = head;
        while (cur != null)
        {
            System.out.print(cur.value + " ");
            cur = cur.next;
        }
        System.out.println();
    }

}

在这里插入图片描述

CD111 判断一个链表是否为回文结构

描述

给定一个链表,请判断该链表是否为回文结构。

输入描述:

n 表示链表的长度

ai 表示链表的各个节点的值。

输出描述:

如果为回文结构输出 “true” , 否则输出 “false”。

示例1

输入:

4
1 2 2 1

输出:

true

备注:

1 \le n \le 10000001≤n≤1000000
-1000000 \le a_i \le 1000000−1000000≤ai≤1000000

解答

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Stack;

public class Main
{
    public static class Node
    {
        public int value;
        public Node next;
        public Node(int v)
        {
            this.value = v;
        }
    }

    public static boolean isPalindrome1(Node head)
    {
        Stack<Integer> stack = new Stack<>();
        Node cur = head;
        while (cur != null)
        {
            stack.push(cur.value);
            cur = cur.next;
        }
        cur = head;
        while (cur != null)
        {
            if (cur.value != stack.pop())
            {
                return false;
            }
            cur = cur.next;
        }
        return true;
    }

    public static boolean isPalindrome2(Node head)
    {
        if (head == null || head.next == null)
        {
            return true;
        }
        // 利用快慢指针方法将链表右半部分放入栈中和链表左半部分比较
        Node fast = head, slow = head.next; // 注意慢指针slow = head.next; 通过画图得到规律
        Stack<Node> stack = new Stack<>();

        // !!! 我第一次做题把fast.next != null && fast.next.next != null顺序反过来了,
        // 出现了空指针异常的报错
        while (fast.next != null && fast.next.next != null)
        {
            fast = fast.next.next;
            slow = slow.next;
        }
        while (slow != null)
        {
            stack.push(slow);
            slow = slow.next;
        }

        while (! stack.isEmpty())
        {
            if (stack.pop().value != head.value)
            {
                return false;
            }
            head = head.next;
        }
        return true;
    }

    public static void main(String[] args) throws IOException, NullPointerException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] inputData = br.readLine().trim().split(" ");
        int m = Integer.parseInt(inputData[0]);
        inputData = br.readLine().trim().split(" ");
        Node head = new Node(Integer.parseInt(inputData[0]));
        Node cur = head;
        for (int i = 1; i < m; i++)
        {
            cur.next = new Node(Integer.parseInt(inputData[i]));
            cur = cur.next;
        }
        // System.out.println(isPalindrome1(head));
        System.out.println(isPalindrome2(head));
    }
}

在这里插入图片描述

CD112 判断一个链表是否为回文结构(进阶)

描述

给定一个链表,请判断该链表是否为回文结构。

输入描述:

n 表示链表的长度。
ai 表示链表节点的值

输出描述:

如果为回文结构输出 “true” , 否则输出 “false”。

示例1

输入:

5
1 2 3 2 1

输出:

true

备注:

1 \le n \le 20000001≤n≤2000000
-1000000 \le a_i \le 1000000−1000000≤ai≤1000000

解答

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main
{
    public static class Node
    {
        public int value;
        public Node next;
        public Node (int value)
        {
            this.value = value;
        }
    }
    public static boolean isPalindromePlus(Node head)
    {
        if (head == null || head.next == null)
        {
            return true;
        }
        Node node1 = head, node2 = head; // node1为快指针,node2为慢指针
        while (node1.next != null && node1.next.next != null)
        {
            node1 = node1.next.next;
            node2 = node2.next;
        }
        // 此时node1到了最后, node2到了中间(后半段开头在node2+1节点处)
        node1 = node2.next;
        node2.next = null;
        Node node3 = null;
        while (node1 != null)
        {
            node3 = node1.next;
            node1.next = node2;
            node2 = node1;
            node1 = node3;
        }
        // node2为最后一个节点
        node3 = node2; // 保存最后一个节点
        node1 = head;
        boolean res = true;
        while (node1 != null && node2 != null)
        {
            if (node1.value != node2.value)
            {
                res = false;
                break;
            }
            node1 = node1.next;
            node2 = node2.next;
        }
        // 恢复原来的链表
        node1 = node3.next;
        node3.next = null;
        while(node1 != null)
        {
            node2 = node1.next;
            node1.next = node3;
            node3 = node1;
            node1 = node2;
        }
        return res;
    }
    public static void main(String[] args) throws IOException, NullPointerException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] inputData = br.readLine().trim().split(" ");
        int m = Integer.parseInt(inputData[0]);
        inputData = br.readLine().trim().split(" ");
        Node head = new Node(Integer.parseInt(inputData[0]));
        Node cur = head;
        for (int i = 1; i < m; i++)
        {
            cur.next = new Node(Integer.parseInt(inputData[i]));
            cur = cur.next;
        }
        // System.out.println(isPalindrome1(head));
        System.out.println(isPalindromePlus(head));
    }
}

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值