《剑指offer》算法题第十一天

今日题目:

  1. 滑动窗口的最大值
  2. 扑克牌中的顺子
  3. 圆圈中最后剩下的数字
  4. 求1+2+3+...+n
  5. 不用加减乘除做加法
  6. 构建乘积数组

今天的题目比较有意思,可以学到很多知识,包括第1题中的数据结构——双向队列,第3题约瑟夫环问题等。

1.滑动窗口的最大值

题目描述:
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

思路:比较直接的方法是维护一个窗口,然后对于每个窗口找出当前窗口的最大值。这种方法的时间复杂度为O(kn)其中k为窗口大小。另外一种方法是,利用队列,在队列中放置有可能为最大值的对象,将当前的最大值放在队首,这样就可以在O(1)的时间内得到最大值,这需要用到双向队列。

代码如下:

 1 /**
 2 用一个双端队列,队列第一个位置保存当前窗口的最大值,当窗口滑动一次
 3 1.判断当前最大值是否过期
 4 2.新增加的值从队尾开始比较,把所有比他小的值丢掉
 5 */
 6 import java.util.ArrayDeque;
 7 import java.util.ArrayList;
 8 public class Solution {
 9     public ArrayList<Integer> maxInWindows(int [] num, int size)
10     {
11         ArrayList<Integer> res = new ArrayList();
12         if(size == 0) return res;
13
14         ArrayDeque<Integer> queue = new ArrayDeque();
15         int begin = 0;
16         for(int i = 0; i < num.length; i++){
17             begin = i - size + 1;
18             if(queue.isEmpty())
19                 queue.add(i);
20             else if(begin > queue.peekFirst())
21                 queue.pollFirst();
22
23             while(!queue.isEmpty() && num[queue.peekLast()] <= num[i])
24                 queue.pollLast();
25             queue.add(i);
26
27             if(begin >= 0)
28                 res.add(num[queue.peekFirst()]);
29         }
30         return res;
31
32     }
33 }

2. 扑克牌中的顺子

题目描述:
从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为0,可以看成任意数字。

思路:比较直接,由于0可以代替任何数字,我们先把数组排序一下,然后计算0的个数,最后判断0的个数是否大于不连续的数字间隔数。

代码如下:

 1 import java.util.*;
 2 public class Solution {
 3     public boolean isContinuous(int [] nums) {
 4         if(nums.length == 0)
 5             return false;
 6         Arrays.sort(nums);
 7         int count_zero = 0;
 8         int count_no_con = 0;
 9         for(int i = 0; i < nums.length-1; i++){
10             if(nums[i] == 0)
11                 count_zero++;
12             else if(nums[i+1]==nums[i])
13                 return false;
14             else{
15                 if(nums[i+1]-nums[i] > 1){
16                     count_no_con += (nums[i+1]-nums[i]-1);
17                 }
18             }
19         }
20         return count_zero >= count_no_con;
21     }
22 }

3.圆圈中最后剩下的数字

题目描述:
0,1,2,...,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。

思路:这是有名的约瑟夫环问题,《剑指》中给出了两种解题方法:一种是用环形链表模拟圆圈;另一种是分析每次被删除的数字的规律并直接计算出圆圈中最后剩下的数字。下面只贴出第二种算法,它是基于递归实现的,推导出递归方程的过程比较复杂,这边就不作阐述,感兴趣的朋友可参考:https://www.nowcoder.net/questionTerminal/f78a359491e64a50bce2d89cff857eb6

代码如下:

1 public class Solution {
2     public int LastRemaining_Solution(int n, int m) {
3         if(n == 0) return -1;
4         if(n == 1) return 0;
5         return (LastRemaining_Solution(n-1,m)+m)%n;
6     }
7 }

4. 求1+2+3+...+n

题目描述:
求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

思路:1. 运用数学公式 (1+n)*n/22. 递归,递归出口利用且(&&)运算的短路特性。

代码如下:

1 //利用且(&&)运算的短路特性
2 public class Solution {
3     public int Sum_Solution(int n) {
4         int sum = n;
5         boolean ans = (n > 0) && (sum += Sum_Solution(n-1))>0;
6         return sum;
7     }
8 }

5. 不用加减乘除做加法

题目描述:
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

思路:利用位运算来模拟加法运算,先做不进位的加法(异或),再算出进位(与,左移一位),然后再做和与进位的加法。其实这是一个递归的思想。

代码如下:

 1 public class Solution {
 2     public int Add(int num1,int num2) {
 3         int sum = 0;
 4         int carry = 0;
 5         do{
 6             sum = (num1 ^ num2);
 7             carry = (num1 & num2) << 1;
 8             num1 = sum;
 9             num2 = carry;
10         }while(carry != 0);
11         return num1;
12     }
13 }

6. 构建乘积数组

题目描述:
给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。

思路:一种比较直接的思路是用double loop来构建乘积数组,简单,但是时间复杂度为O(n2)。另一种解决方法是将A[0]*A[1]*...*A[i-1]以及A[i+1]*...*A[n-1]分别存起来,最后再进行整合。

代码如下:

 1 import java.util.ArrayList;
 2 public class Solution {
 3     public int[] multiply(int[] A) {
 4         int n = A.length;
 5         int[] left = new int[n];
 6         int[] right = new int[n];
 7         left[0] = 1;
 8         for(int i = 1; i < n; i++){
 9             left[i] = left[i-1]*A[i-1];
10         }
11         right[n-1] = 1;
12         for(int i = n-2; i >= 0; i--){
13             right[i] = right[i+1]*A[i+1];
14         }
15         int[] B = new int[n];
16         for(int i = 0; i < n; i++){
17             B[i] = 1;
18             B[i] = left[i]*right[i];
19         }
20         return B;
21     }
22 }

原文地址:https://www.cnblogs.com/wezheng/p/8428191.html

时间: 2024-08-26 16:07:11

《剑指offer》算法题第十一天的相关文章

《剑指Offer》题三十一~题四十

三十一.栈的压入.弹出序列 题目:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序.假设压入栈的数字均不相等.例如,序列{1, 2, 3, 4 ,5}是某栈的压栈序列,序列{4, 5, 3, 2, 1}是该压栈序列对应的一个弹出序列,但{4, 3, 5, 1, 2}就不可能是该压栈序列的弹出序列. 分析:本题中的压栈序列并非是一次全部压入堆栈!如果没有思路,可以举一两个例子,一步步分析压栈.弹出的过程,从中找出规律. 三十二.从上到下打印二叉树 题目一:不分行从

《剑指Offer》题四十一~题五十

四十一.数据流中的中位数 题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值. 四十二.连续子数组的最大和 题目:输入一个整型数组,数组里有正数也有负数.数组中的一个或连续多个整数组成一个子数组.求所有子数组的和的最大值.要求时间复杂度为O(n). 四十三.1~n整数中1出现的次数 题目:输入一个整数n,求1~n这n个整数的十进制表示中1出现的次数.例如,输

剑指offer算法总结

剑指offer算法学习总结 节选剑指offer比较经典和巧妙的一些题目,以便复习使用.一部分题目给出了完整代码,一部分题目比较简单直接给出思路.但是不保证我说的思路都是正确的,个人对算法也不是特别在行,只不过这本书的算法多看了几遍多做了几遍多了点心得体会.于是想总结一下.如果有错误也希望能指出,谢谢. 具体代码可以参考我的GitHub仓库: https://github.com/h2pl/SwordToOffer 数论和数字规律 从1到n整数中1出现的次数 题目描述 求出1~13的整数中1出现的

剑指 offer 第一题: 二维数组中的查找

打算写 图解剑指 offer 66 题 的系列文章,不知道大家有没有兴趣 ?? 题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 题目分析 图 1 如果没有头绪的话,很显然使用 暴力解法 是完全可以解决该问题的. 即遍历二维数组中的每一个元素,时间复杂度:O(n^2). 其实到这里我们就可以发现,使用这种暴力解法并没有充分利用题目给出的信息.这

剑指offer链表题的双指针法总结

本篇博客旨在总结双指针法在剑指offer链表题中的应用 包括删除链表中重复的节点.链表中倒数第k个节点.链表中环的入口节点.反转链表.合并两个排序的链表.两个链表的第一个公共节点. 根据双指针的类型,可以大致分为三种: 第一种是间隔一定距离的双指针法,包括删除链表中重复的节点.链表中倒数第k个节点两题 删除链表中重复的节点 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3->3->4->4->5

剑指offer编程题Java实现——面试题12相关题大数的加法、减法、乘法问题的实现

用字符串或者数组表示大数是一种很简单有效的表示方式.在打印1到最大的n为数的问题上采用的是使用数组表示大数的方式.在相关题实现任意两个整数的加法.减法.乘法的实现中,采用字符串对大数进行表示,不过在具体的计算中,还是要将字符串转化成字符数组来进行计算. 实现两个大数的加法,要考虑到两个问题,两个数的和的位数问题,以及如何处理两个数按位相加产生的进位问题.首先两个整数相加,两个数的和的位数最多比最大的整数的位数多1:这样和的位数就确定了.对于进位问题,我的做法是先进行按位相加,相加操作完成后再按照

剑指offer编程题Java实现——面试题9斐波那契数列

题目:写一个函数,输入n,求斐波那契数列的第n项. 1 package Solution; 2 3 /** 4 * 剑指offer面试题9:斐波那契数列 5 * 题目:写一个函数,输入n,求斐波那契数列的第n项. 6 * 0, n=1 7 * 斐波那契数列定义如下:f(n)= 1, n=2 8 * f(n-1)+f(n-2), n>2 9 * @author GL 10 * 11 */ 12 public class No9Fibonacci { 13 14 public static void

剑指offer编程题Java实现——面试题7相关题用两个队列实现一个栈

剑指offer面试题7相关题目:用两个队列实现一个栈 解题思路:根据栈的先入后出和队列的先入先出的特点1.在push的时候,把元素向非空的队列内添加2.在pop的时候,把不为空的队列中的size()-1份元素poll出来,添加到另为一个为空的队列中,再把队列中最后的元素poll出来两个队列在栈不为空的情况下始终是有一个为空,另一个不为空的.push添加元素到非空的队列中,pop把非空队列的元素转移到另一个空的队列中,直到剩下最后一个元素,这个元素就是要出栈的元素(最后添加到队列中的元素). 1

剑指offer编程题Java实现——面试题14调整数组顺序使奇数位于偶数之前

题目: 输入一个整数数组,实现一个函数来调整该数组中数组的顺序,使得所有的奇数位于数组的前半部分,偶数位于数组的后半部分. 解题思路:数组中维护两个指针,第一个指针初始化时候指向数组头部,第二个指针初始化时候指向数组尾部,第一个指针指向的数字总是偶数,第二个指针指向的数字总是奇数,如果第一个指针在第二个指针之前,则交换两指针指向的元素. 1 package Solution; 2 3 /** 4 * 剑指offer面试题14:调整数组顺序是奇数位于偶数前面 5 * 题目:输入一个整数数组,实现一

剑指offer编程题Java实现——面试题10二进制中1的个数

题目: 请实现一个函数,输入一个整数,输出该整数二进制表示中1的个数.例如,把9表示成二进制是1001,有2位是1,该函数输出2解法:把整数减一和原来的数做与运算,会把该整数二进制表示中的最低位的1变成0,与运算进行多少次就有多少个1. 1 package Solution; 2 /** 3 * 剑指offer面试题10:二进制中1的个数 4 * 题目:请实现一个函数,输入一个整数,输出该整数二进制表示中1的个数. 5 * 例如,把9表示成二进制是1001,有2位是1,该函数输出2 6 * 解法