文章目录
牛客网剑指offer 67题(12-21)
(二)12-21题,早上院里安排了模拟复试,负责拉考生进考场和录像监考机位,一个组45个人跳过专业课面试的模拟都弄了快3个小时,中午又去接待了同学,下午直接瘫倒在床上,以后到正式线上复试了肯定更久…卑微打工仔,不过之前觉得线上复试还是很容易出问题,现在想想在双机位的存在下还是比较公平的,有人说线上复试可以线上组队交流,这种可能性存在,但线下复试你也没法阻止抱团的感觉,有时候确定得靠考生的自觉,今天做的题也还行,因为还要搞论文实验的事,写的也比较马虎,有几道题的逻辑性还是很缜密的,也没有去想什么优化的方法了。
12.数值的整数次方
题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0
思路
可以考虑使用快速幂实现,x的n次方首先可以先看n(x若为负数就直接取倒数),n为偶数则直接可以为xx的n/2次方,n为奇数则可以表示为xx的n-1次方(无需考虑x是否为正)
递归实现
public class Solution {
public double Power(double base, int exponent) {
if(exponent==0) return 1;
else if(exponent>0&&exponent%2==0) return Power(base*base,exponent/2);
else if(exponent>0&&exponent%2!=0) return Power(base,exponent-1)*base;
else return 1/Power(base,-exponent);
}
}
迭代实现
public class Solution {
public double Power(double base, int exponent) {
double num=1.0;
for(int i=Math.abs(exponent);i!=0;i/=2){
if(i%2!=0)
num*=base;
base*=base;
}
return exponent<0?1/num:num;
}
}
13.调整数组顺序是奇数位于偶数前面
题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
思路
首先确定数组中奇数偶数的个数,设置奇数跟偶数开始的索引,复制一个数组,通过复制的数组判定奇偶数替换到原数组中。
public class Solution {
public void reOrderArray(int [] array) {
int oddNums=0;
for(int num:array){
if(num%2!=0)
oddNums++;
}
int []newArr=array.clone();
int oddIndex=0,evenIndex=oddNums;
for(int i=0;i<newArr.length;i++){
if(newArr[i]%2!=0)
array[oddIndex++]=newArr[i];
else
array[evenIndex++]=newArr[i];
}
}
}
14.链表中倒数第k个结点
题目描述
输入一个链表,输出该链表中倒数第k个结点。
思路
双指针思路,既然是倒数第k个结点,设置两个指针p,q指向第一个结点,先让指针p先向后移动k次,然后pq同步向后移动,直至p指向空,此时q就指向了倒数第k个结点。
/*
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(head==null) return null;
ListNode p=head,q=head;
while(p!=null&&k>0){
p=p.next;
k--;
}
if(k>0) return null;//表示k值超过链表结点数
while(p!=null){
p=p.next;
q=q.next;
}
return q;
}
}
15.反转链表
题目描述
输入一个链表,反转链表后,输出新链表的表头。
思路
反转链表应该是数据结构中单链表的一个必掌握的知识,只要设定好指针即可(当前结点的前驱结点和后继结点),而且遇到链表的题,自己动手画一画实现过程会让思路更加清晰。
public class Solution {
public ListNode ReverseList(ListNode head) {
ListNode pre=head,cur=null,q=null;
while(pre!=null){
q=pre.next;
pre.next=cur;
cur=pre;
pre=q;
}
return cur;
}
}
16.合并两个排序的链表
题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
思路
这一题也比较容易实现了,递归跟迭代都是可以的,迭代的话可以设置的新链表,每次加入两个链表中较小的值并更新链表。
迭代实现
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
ListNode newList=new ListNode(-1);
ListNode pre=newList;
while(list1!=null&&list2!=null){
if(list1.val<=list2.val){
pre.next=list1;
list1=list1.next;
}
else{
pre.next=list2;
list2=list2.next;
}
pre=pre.next;
}
if(list1!=null){
pre.next=list1;
}
if(list2!=null)
pre.next=list2;
return newList.next;
}
}
递归实现
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
if(list1==null) return list2;
if(list2==null) return list1;
if(list1.val<=list2.val) {
list1.next=Merge(list1.next,list2);
return list1;
}
else{
list2.next=Merge(list1,list2.next);
return list2;
}
}
}
17.树的子结构
问题描述
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
思路
用到了双层递归,在递归比较当前是否满足子结构时还需要递归比较左右子树是否满足。
PS:这一题最开始以为是跟判定子树一样的,发现测试用例输入是错误的,所以写题的时候子结构和子树对逻辑判断条件是稍微有些区别的。
public class Solution {
public boolean HasSubtree(TreeNode root1,TreeNode root2) {
if(root1==null||root2==null) return false;
return isSubtree(root1,root2)||HasSubtree(root1.left,root2)||HasSubtree(root1.right,root2);
}
public boolean isSubtree(TreeNode root1,TreeNode root2){
if(root1==null&&root2==null) return true;
if(root1==null) return false;
if(root2==null) return true;
//if(root1==null||root2==null) return false;这一个逻辑条件是判断是否为子树使用
if(root1.val!=root2.val) return false;
return isSubtree(root1.left,root2.left)&&isSubtree(root1.right,root2.right);
}
}
18. 二叉树的镜像
题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。
思路
递归实现交换二叉树的左右子树。
public class Solution {
public void Mirror(TreeNode root) {
if(root==null) return;
swap(root);
Mirror(root.left);
Mirror(root.right);
}
public void swap(TreeNode root){
TreeNode temp=root.left;
root.left=root.right;
root.right=temp;
}
}
19.顺时针打印矩阵
题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下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.
思路
从外向里以顺时针顺序输出,那么就会有四个方向的遍历,分别是上部向右,右部向下,下部向左,左部向上(右下左上四个方向变量),只需要设定好索引位置循环遍历即可,注意当每次遍历一个方向之后对应的变量值要变更,避免重复遍历(这个代码只能手打没有提示,所以之前粗心打错过好几次变量)。
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printMatrix(int [][] matrix) {
ArrayList<Integer> list=new ArrayList<>();
if(matrix==null) return list;
int rowUp=0,rowDown=matrix.length-1;//定义右、下初始变量
int colLeft=0,colRight=matrix[0].length-1;//定义左、上初始变量
while(colLeft<=colRight&&rowUp<=rowDown){//定义循环结束即遍历完成条件
for(int i=colLeft;i<=colRight;i++){//右移遍历
list.add(matrix[rowUp][i]);
}
for(int i=rowUp+1;i<=rowDown;i++){//下移遍历
list.add(matrix[i][colRight]);
}
if(rowUp!=rowDown){//是否已到同一行
for(int i=colRight-1;i>=colLeft;i--){//左移遍历
list.add(matrix[rowDown][i]);
}
}
if(colLeft!=colRight){//是否已到同一列
for(int i=rowDown-1;i>rowUp;i--){//上移遍历
list.add(matrix[i][colLeft]);
}
}
rowUp++;
rowDown--;
colLeft++;
colRight--;
}
return list;
}
}
20.包含min函数的栈
题目描述
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
注意:保证测试中不会当栈为空的时候,对栈调用pop()或者min()或者top()方法。
思路
设立一个辅助栈minStack,用该存储stack栈中最小值,栈顶元素为当前最小值,如果找到比它更小的就入栈,输出minStack栈顶元素即为最小值。
import java.util.Stack;
public class Solution {
private Stack<Integer> stack=new Stack<>();
private Stack<Integer> minStack=new Stack<>();
public void push(int node) {//当node值小于minStack栈顶值时才入栈。
stack.push(node);
if(!minStack.isEmpty()){
int top=minStack.peek();
if(node<=top)
minStack.push(node);
}else
minStack.push(node);
}
public void pop() {
int pop=stack.pop();
int top = minStack.peek();
//等于的时候再出栈
if (pop == top) {
minStack.pop();
}
}
public int top() {
return stack.peek();
}
public int min() {
return minStack.peek();
}
}
21.栈得压入、弹出序列
题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列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) {
Stack<Integer> stack =new Stack<>();
int popIndex=0;
for(int i=0;i<pushA.length;i++){
stack.push(pushA[i]);//按pushA入栈
while(popIndex<popA.length&&popA[popIndex]==stack.peek()){//执行循环栈顶元素与当前popA索引值相等则出栈;
stack.pop();
popIndex++;
}
}
return stack.isEmpty();//如果栈空说明出栈顺序存在
}
}