牛客网剑指offer

本文汇总了牛客网剑指offer中的经典算法问题,包括二维数组查找、字符串空格替换、链表操作、数组旋转、斐波那契数列等,旨在提升编程思维和算法能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以下代码都是我提交过的正确的代码

  • 二维数组中的查找

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

 public static boolean Find(int target, int[][] array) {
     int height = array.length;
     int width = array[0].length;

     int i = height - 1;
     int j = 0;

     while(i >= 0 && j < width){
         if(array[i][j] == target)
             return true;
         else if(array[i][j] > target){
             i--;
         }
         else
             j++;
     }
     return false;
 }
  • 替换空格

请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

//Java版
static String replaceSpace(StringBuffer str) {
        if(str == null)
            return null;
        StringBuilder newStr = new StringBuilder();
        for(int i = 0; i < str.length();i++){
            if(str.charAt(i) == ' '){
                newStr.append("%20");
            }
            else
                newStr.append(str.charAt(i));
        }
        return newStr.toString();
    }
//js版
function replaceSpace(str){
    if(str == null)
      return null;
    var reg = /\s/g;//全局替换空格
    var result = str.replace(reg,"%20");
    return result;
}
  • 从尾到头打印链表

输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

public class ListNode {
     int val;
     ListNode next = null;
   	 ListNode(int val) {
     this.val = val;
   }
}
//用栈存链表的节点
public ArrayList<Integer> printListFromTailToHead(ListNode listNode){
   Stack<Integer> stack = new Stack<>();
    while(listNode != null){
        stack.push(listNode.val);
        listNode = listNode.next;
    }
    ArrayList<Integer> arrayList = new ArrayList<>();
    while (!stack.isEmpty()){
        arrayList.add(stack.pop());
    }
    return arrayList;
}
  • 用两个栈实现队列

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

import java.util.Stack;

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    
    public void push(int node) {
        stack1.push(node);
    }

    public int pop() {
        int first = stack1.pop();
        while(!stack1.isEmpty()){
            stack2.push(first);
            first = stack1.pop();
        }
        while(!stack2.isEmpty()){
            stack1.push(stack2.pop());
        }
        return first;
    }
}
  • 旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

/*非递减数组:数组中每个元素大于等于上一个元素,所以非递减数组的旋转数组以最小的数字为界,前后都是非递减数组,并且最小的数组小于前面的所有数字,开始递减的位置就是最小数字的索引位置*/
public static int minNumberInRotateArray(int [] array) {
    int length = array.length;
    if(length ==0)
        return 0;
    for(int i = 0; i < length - 1; i++){
        if(array[i] > array[i+1])
            return array[i+1];
    }
    return array[0];
}
  • 斐波那契数列

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39

public static int Fibonacci(int n) {
    int sum = 0;
    int pre1 = 1; //前一个
    int pre2 = 1; //前两个个
    if(n <= 0)
        return 0;
    if(n == 1 || n ==2)
        return 1;
    else {
        for(int i = 3; i < n + 1; i++){
            sum = pre1 + pre2;
            pre1 = pre2;
            pre2 = sum;
        }
    }
    return sum;
}
  • 跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

//斐波那契数列 1 2 3 5 8 13 21
// 一次只能走一步或者两步,拿f(4)来说,f(3)走的路径f(4)也能走,f(2)走的路径f(4)也能走,所以f(4)=f(2)+f(3)
public static int JumpFloor(int target) {
    int sum = 0;
    int pre1 = 1; //前一个
    int pre2 = 2; //前两个
    if(target <= 0) return 0;
    if(target == 1) return 1;
    if(target == 2) return 2;
    else {
        for(int i = 2; i < target; i++){
            sum = pre1 + pre2;
            pre1 = pre2;
            pre2 = sum;
        }
    }
    return sum;
}
  • 变态跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

/************解一***********/
//f(n)=f(n-1)+f(n-2)+f(n-3)+...+f(1)+1
public int JumpFloorII(int target) {
    if(target == 0) return 0;

    int[] bt = new int[target + 1];

    bt[0] = 1;
    bt[1] = 1;

    for(int i = 2; i <= target; i++){
        bt[i] = 0;
        for(int j = 0; j < i; j++){
            bt[i] += bt[j];
        }
    }
    return bt[target];
}
  • 矩形覆盖

我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

//这个题类似于简单的跳台阶,f(n)=f(n-1)+f(n-2)
public static int RectCover(int target) {
   int num = 0;
   if(target == 0 || target == 1 || target == 2)return target;
   int pre1 = 1; // 第一张
   int pre2 = 2; // 第二张
   for(int i = 3; i <= target; i++){
       num = pre1 + pre2;
       pre1 = pre2;
       pre2 = num;
   }
   return num;
}
/************解二***********/
public static int JumpFloor(int target) {
    if(target == 0) return 0;

    int[] bt = new int[target + 1];

    bt[0] = 1;
    bt[1] = 1;

    for(int i = 2; i <= target; i++){
    //既然f(n)=f(n-1)+f(n-2)+...+f(1)+1,那么f(n+1)=f(n)+f(n)-1+1
        bt[i] = bt[i-1] + bt[i-1] - 1 + 1;
    }
    return bt[target];
}
  • 二进制中1的个数

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

public class Solution {
    public static int NumberOf1(int n) {
        int num = 0;
        char[] charArray = Integer.toBinaryString(n).toCharArray();
        for(int i = 0; i < charArray.length; i++){
            if(charArray[i] == '1'){
                num++;
            }
        }
        return num;
    }
}
  • 数值的整数次方

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

public static double Power(double base, int exponent) {
//java中浮点数的范围为-Double.MAX_VALUE~-Double.MIN_NORMAL
//以及Double.MIN_NORMAL~-Double.MAX_VALUE
   double num = 1.0;
   if(base == 0) return 0;
   int tempExponent = Math.abs(exponent);
   for(int i = 0; i < tempExponent; i++){
       num *= base;
       if(num > Double.MAX_VALUE) {
           System.out.println("ERROR超出最大浮点数的范围");
           return -1;
       }
       if(num < -Double.MAX_VALUE) {
           System.out.println("ERROR超出最小浮点数的范围");
           return -1;
       }
       if(num > -Double.MIN_NORMAL && num < Double.MIN_NORMAL){
           System.out.println("ERROR在最大负浮点数和最小正浮点数的范围内,不被允许");
           return -1;
       }
   }
   //根据指数的正负来判断结果是否是其倒数
   return exponent > 0?num:1/num;
}
  • 调整数组顺序使奇数位于偶数前面

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

/**********************解1**********************/
//首先找到最开始的那个偶数的a,下标是n1,然后找到离这个偶数最近的奇数b,下标是n2,让a到b-1之间的数字向后移动一位,奇数b移到n1处,继续
public void reOrderArray(int [] array) {
   int length = array.length;
   for(int i = 0; i < length; i++){
       int index = 0;
       boolean flag = false;
       if(array[i]%2==0){
           for(int j = i + 1; j < length; j++){
               if(array[j] % 2 ==1){
                   index = j;
                   flag = true;
                   break;
               }
           }
           if(flag){
               int temp = array[index];
               for(int m = index; m > i; m--){
                   array[m] = array[m-1];
               }
               array[i] = temp;
           }
       }
   }
}
/**********************解2**********************/
//新开辟两个数组,将偶数和奇数分别存入两个数组,最后拼接,这种方法时间复杂度是最小的
public class Solution {
  public void reOrderArray(int [] array) {
       int length = array.length;
       int[] even = new int[length];
       int[] odd = new int[length];

       int evenIndex = 0;
       int oddIndex = 0;

       for(int i = 0; i < length; i++){
           if(array[i] % 2 == 0){
               even[evenIndex] = array[i];
               evenIndex++;
           }
           else{
               odd[oddIndex] = array[i];
               oddIndex++;
           }
       }
       for(int i = 0; i < oddIndex; i++){
           array[i] = odd[i];
       }
       for(int i = oddIndex; i < length; i++)
           array[i] = even[i - oddIndex];
   }
}
  • 链表中倒数第k个结点

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

//先统计节点的个数,然后循环找到倒数第k个节点
public ListNode FindKthToTail(ListNode head,int k) {
    int count = 0;
    ListNode tempNode = head;
    while(tempNode != null){
        count++;
        tempNode = tempNode.next;
    }
    if(count < k)
        return null;
    ListNode p = head;
    for(int i = 1; i <= count - k; i++){
        p = p.next;
    }
    return p;
}
  • 反转链表

输入一个链表,反转链表后,输出新链表的表头。

//存下当前节点的下一节点,当前节点的下一节点指向前一节点,前一节点等于当前节点,当前节点等于存下的下一节点。
public ListNode ReverseList(ListNode head) {
        ListNode pre = null;
        ListNode next = null;

        if(head == null)
            return null;

        while(head != null){
            next = head.next;  
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }
  • 合并两个排序的链表

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

//一个简单的递归
public ListNode Merge(ListNode list1,ListNode list2) {
        ListNode pHead = null;
        if(list1 == null)return list2;
        if(list2 == null)return list1;

        if(list1.val < list2.val){
            pHead = list1;
            pHead.next = Merge(list1.next, list2);
        }else {
            pHead = list2;
            pHead.next = Merge(list1, list2.next);
        }
        return pHead;
    }
  • 二叉树的镜像

操作给定的二叉树,将其变换为源二叉树的镜像。

public void Mirror(TreeNode root) {
    if(root == null) return;
    if(root.left == null && root.right == null) return;

	//交换左右子树
    TreeNode treeNode = null;
    treeNode = root.left;
    root.left = root.right;
    root.right = treeNode;

    if(root.left != null)
        Mirror(root.left);
    if(root.right != null)
        Mirror(root.right);
}
//树的结构
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }
  • 顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

//查找的顺序是 left->right top->bottom  right->left   bottom->top 控制好边界条件
    public ArrayList<Integer> printMatrix(int [][] matrix) {
        ArrayList<Integer> arrayList = new ArrayList<Integer>();

        int row = matrix.length;    //行
        int col = matrix[0].length; //列

        int left = 0, top = 0, right = col - 1, bottom = row - 1;

        //循环结束条件
        while(left <= right && top <= bottom){
            //left -> right
            for(int i = left; i <= right; i++) arrayList.add(matrix[top][i]);
            //top -> bottom
            for(int i = top + 1; i <= bottom; i++) arrayList.add(matrix[i][right]);
            //right -> left
            if(top != bottom)  //从left -> right就结束了,防止再次计算
            for(int i = right - 1; i >= left; i--) arrayList.add(matrix[bottom][i]);
            //bottom -> top
            if(left != right) //从top -> bottom就结束了,防止再次计算
            for(int i = bottom - 1; i > top; i--)arrayList.add(matrix[i][left]);
            left++;right--;top++;bottom--;
        }
        return arrayList;
    }
  • 包含min函数的栈

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。

import java.util.Stack;

public class Solution {
    Stack<Integer> stack = new Stack<Integer>();
    Stack<Integer> minstack = new Stack<Integer>();
    public void push(int node) {
        stack.push(node);
        if(minstack.empty())
            minstack.push(node);
        //判断新押入stack的数值是否小于等于minstack的栈顶
        else if(node <= minstack.peek())
            minstack.push(node);

    }
    //如果minstack的栈顶和原stack栈顶一样大,弹出minstack的栈顶
    public void pop() {
        if(stack.peek() == minstack.peek())
            minstack.pop();
        stack.pop();
    }
    //获取stack的栈顶
    public int top() {
        return stack.peek();
    }
    //获取minstack的栈顶
    public int min() {
        return minstack.peek();
    }
}
  • 栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

import java.util.ArrayList;
import java.util.Stack; //不要忘记导包!!!
 //模拟弹出的过程
public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
      if(pushA.length != popA.length || pushA.length == 0 || popA.length == 0)
            return false;
        Stack<Integer> tempStack = new Stack<Integer>();
        int index = 0;
        for(int i = 0; i < pushA.length; i++){
            tempStack.push(pushA[i]);
            while(index < popA.length && tempStack.peek() == popA[index]){
                tempStack.pop();
                index++;
            }
        }
        return tempStack.isEmpty()?true:false;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值