剑指Offer编程整理(五)
来源:互联网 发布:淘宝企业店铺 申请 编辑:程序博客网 时间:2024/05/04 09:51
1、扑克牌顺子
2、孩子们的游戏(圆圈中最后剩下的数)
3、求1+2+3+...+n
4、不用加减乘除做加法
5、把字符串转换成整数
6、数组中重复的数字
7、构建乘积数组
8、正则表达式匹配
9、表示数值的字符串
10、字符流中第一个不重复的字符
11、链表中环的入口结点
1、扑克牌顺子
(1)问题描述:
LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子.....LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何。为了方便起见,你可以认为大小王是0。
(2)解题思路:
统计0的个数,看相邻的数空缺是否为0的个数。
(3)代码实现:
import java.util.Arrays;public class Solution { public boolean isContinuous(int [] numbers) { if (numbers == null || numbers.length != 5){ return false; } Arrays.sort(numbers); int numberOfZero = 0; int numberOfGap = 0; //统计0的个数 for (int i=0 ; i<numbers.length&&numbers[i]==0;i++){ numberOfZero++; } int small = numberOfZero; int big = small+1; //看相邻的数空缺是否为0的个数; while (big < numbers.length){ if (numbers[small] == numbers[big]){ return false; } numberOfGap += (numbers[big] - numbers[small]-1); small = big; big++; } return numberOfGap <= numberOfZero; }}
2、孩子们的游戏(圆圈中最后剩下的数)
(1)问题描述:
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
(2)解题思路:
法一:数组实现,每删除第m个位置的值,得到一个新的数组(第m个位置后的值放在前面,第m个位置前的值放在后面),直到只剩最后一个值。
法二:定义一个循环链表,将总数添加到循环链表中;循环遍历链表,删除第m个结点;直到最后一个结点,返回该结点值。
(3)代码实现:
法一:
public int LastRemaining_Solution(int n, int m) { if(m == 0) return -1; int[] a = new int[n]; for(int i=0; i<n; i++) a[i] = i; for(int i=0; i<n-1; i++){ int len = a.length; int index = (m - 1) % len; int[] aux = new int[len-1]; int j = 0; for(int k=index+1; k<len; k++) aux[j++] = a[k]; for(int k=0; k<index; k++) aux[j++] = a[k]; a = aux; aux = null; } return a[0]; }
法二:
public int LastRemaining_Solution(int n, int m) { Node header = new Node(0); Node pointer = header; for (int i=1;i<n;i++){ pointer.next = new Node(i); pointer = pointer.next; } pointer.next = header; while (pointer != pointer.next){ for (int i=0;i<m-1;i++){ pointer = pointer.next; } pointer.next = pointer.next.next; } return pointer.next.no; }
3、求1+2+3+...+n
(1)问题描述:
求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
(2)解题思路:
递归
(3)代码实现:
public int Sum_Solution(int n) { int sum = n; boolean ans = (n>0)&&((sum+=Sum_Solution(n-1))>0); return sum; }}
4、不用加减乘除做加法
(1)问题描述:
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
(2)解题思路:
法一:计数法
法二:位运算
(3)代码实现:
法一:
public int Add(int num1,int num2) { while (num2!=0) { num1++; num2--; } return num1; }
法二:
public int add(int num1,int num2) { int Sum, Carry; do { Sum = num2 ^ num1; Carry = (num1 & num2) << 1; num2 = Carry; num1 = Sum; } while (num2 != 0); return num1; }
5、把字符串转换成整数
(1)问题描述:
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
(2)解题思路:
法一:从高位到低位
法二:从低位到高位
(3)代码实现:
法一:
public int StrToInt(String str) { if (str.length()==0 || str.equals("")){ return 0; } char symble = str.charAt(0); int result = 0; if (symble >= '0' && symble <= '9'){ result += symble - '0'; }else if (!(symble == '-' || symble == '+')){ return 0; } for (int i=1;i<str.length();i++){ if (str.charAt(i)>='0' && str.charAt(i)<='9'){ int value = str.charAt(i)-'0'; result = result*10 + value; }else { return 0; } } return str.charAt(0)=='-'?-result:result; }
法二:
public int StrToInt(String str) { if(str==null || str.length() == 0){ return 0; } int result = 0; char[] chs = str.toCharArray(); int len = chs.length; for(int i=len-1, j=0; i>0; i--, j++){ int c = (int)chs[i]; if(c<48 ||c>57){ return 0; }else{ result += (c-48)*Math.pow(10, j); } } int c = (int)chs[0]; if(c<=57&&c>=48){ result += (c-48)*Math.pow(10, len-1); } if(result<-2147483648 || result>2147483647){ return 0; //越界,如果真的越界,直接会报错,result本身没办法越界 }else if(str.equals("2147483648")){ if(c == 45){ result = -2147483648; //边界值 } }else if(str.equals("-2147483648")){ result = -2147483648; //边界值 }else{ if(c == 45){ result = -result; //负号处理 } } return result; }
6、数组中重复的数字
(1)问题描述:
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
(2)解题思路:
法一:先将数组进行快排,再看相邻的两个数是否相等。
法二:构造一个容量为N的辅助数组B,原数组A中每个数对应B中下标,首次命中,B中对应元素+1。如果某次命中时,B中对应的不为0,说明,前边已经有一样数字了,那它就是重复的了。
法三:由于所有元素值是有范围的,因此可以用一个长度为n的数组,下标表示序列中的每一个值,下标对应的值表示该下标出现的次数。
只需扫描一次原序列,就统计出所有元素出现的次数;
再扫描一次哈希数组,找到一个出现次数大于1的值即可。
(3)代码实现:
法一:
public boolean duplicate(int numbers[],int length,int [] duplication) { if(numbers == null || numbers.length == 0) return false; Arrays.sort(numbers); int flag = 0;//做标记 for(int i=0;i<length-1;i++) { if(numbers[i] == numbers[i+1]) { duplication[0] = numbers[i]; flag = 1; break; } } return flag == 1? true:false; }
法二:
public boolean duplicate(int numbers[],int length,int [] duplication) { int[] assist = new int [length]; for(int i = 0; i < length; i++){ if(assist[numbers[i]] == 0){ assist[numbers[i]] ++; }else{ duplication[0] = numbers[i]; return true; } } return false; }法三:
public boolean duplicate(int array[],int length,int [] duplication) { if ( array==null ) return false; // 判断数组是否合法(每个数都在0~n-1之间) for ( int i=0; i<length; i++ ) { if ( array[i]<0 || array[i]>length-1 ) { return false; } } // key step int[] hash = new int[length]; for( int i=0; i<length; i++ ){ hash[array[i]]++; } for(int i=0; i<length; i++){ if ( hash[i]>1 ) { duplication[0] = i; return true; } } return false;}
7、构建乘积数组
(1)问题描述:
给定一个数组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]。不能使用除法。
(2)解题思路:
计算前i-1个元素的乘积,及后n-i个元素的乘积, 分别保存在两个数组中。
(3)代码实现:
public int[] multiply(int[] A) { int[] left = new int[A.length]; left[0] = 1; for (int i = 1; i < left.length; i++) { left[i] = A[i-1]*left[i-1]; } int[] right = new int[A.length]; right[right.length-1] = 1; for (int i = right.length-2; i >=0; i--) { right[i] = A[i+1]*right[i+1]; } int[] arr = new int[A.length]; for (int i = 0; i < arr.length; i++) { arr[i] = left[i]*right[i]; } return arr; }
8、正则表达式匹配
(1)问题描述:
请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配
(2)解题思路:
一、如果模式串的下一个字符是*,
1.1 并且模式串的当前字符能与主串的字符进行匹配,则可能出现三种情况:
1、模式串的当前字符匹配到0个字符,则主串不变,模式穿移动到两个字符
2、模式穿的当前字符匹配到1个字符,则主串移动一个位置,模式串移动两个位置
3、模式串的当前字符匹配到多个字符,则主串移动一个位置,模式串移动两个位置。 1.2 如果不能匹配的话: 主串不变,模式串移动两个位置;
二、如果下一个字符不是*,则进行逐个字符进行匹配 三、如果模式串的下一个字符是.,则就进行一个字符的匹配
(3)代码实现:
public class Solution { public boolean match(char[] str, char[] pattern) { if (str == null || pattern == null) return false; return matchRegCore(str, 0, str.length, pattern, 0, pattern.length); } private boolean matchRegCore(char[] str, int i, int length1, char[] pattern, int j, int length2) { if (i == length1 && j == length2) { // 主串匹配到末尾,模式串要么也匹配到末尾要么当前位置的字符是*,否则返回false if (j == length2 || pattern[j] == '*') return true; else return false; } if (i != length1 && j == length2) return false; if (j + 1 < length2 && pattern[j + 1] == '*') { if (i < length1 && (pattern[j] == str[i] || pattern[j] == '.')) { return matchRegCore(str, i + 1, length1, pattern, j, length2) || matchRegCore(str, i + 1, length1, pattern, j + 2, length2) || matchRegCore(str, i, length1, pattern, j + 2, length2); } else { return matchRegCore(str, i, length1, pattern, j + 2, length2); } } if (i < length1 && (str[i] == pattern[j] || pattern[j] == '.')) { return matchRegCore(str, i + 1, length1, pattern, j + 1, length2); } return false; }}
9、表示数值的字符串
(1)问题描述:
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
(2)解题思路:
循序渐进
(3)代码实现:
public static boolean isNumeric(char[] str) { int point = 0;//记录小数点个数 int i = 0;//遍历str数组用的下标 int eFlag = 0;//e/E出现的次数 //判断第一个字符是不是符号,是的话后续的判断就跳过一个数 if(str[0] == '-' || str[0] == '+' ) { i++; } for (; i<str.length; ++i) { if(str[i] == '+' || str[i] == '-'){ //出现字符,若此字符的前一个字符不是e/E则认为是多出来的符号 if (str[i-1] != 'e' && str[i-1] != 'E') return false; continue; } if(str[i] == 'e' || str[i] == 'E') { eFlag++; if(eFlag > 1)//e/E不能出现两次 return false; //若e/E的前一个字符不是数字,或者e/E出现字符串的第一个或者最后一个,都认为是错的 if(i-1<0 || str[i-1] <48 || str[i-1] >57 || i+1>str.length-1 ) return false; point++; continue; } if (str[i] == '.') {//小数点数字不能超过两个 ++point; if (point > 1) { return false; } continue; } //出现非数字且不是e/E则认为是错的(小数点和符号在前面的判断里用“continue”跳过了) if ((str[i] < 48 || str[i] > 57) && (str[i] != 'e') && (str[i] != 'E')) return false; } return true;}另:
public boolean isNumeric(char[] str) { String s = new String(str); if(s.matches("(-|\\+)?\\d*(\\.)?\\d*((E|e)(-|\\+)?\\d+)?")){ return true; } return false; }
10、字符流中第一个不重复的字符
(1)问题描述:
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
(2)解题思路:
法一:用数组。
法二:用list。
(3)代码实现:
法一:
public class Solution { int[] cIndexes = new int[256]; int index = 0; { for (int i = 0; i < cIndexes.length; i++){ cIndexes[i] = -1; } } //Insert one char from stringstream public void Insert(char ch) { //插入数据,判断当前字符是否是第一次出现,是(为-1)则在对应的位置插入 // 当前字符出现在字符流的位置,否则置为已经出现过(-2) if (cIndexes[ch] == -1){ cIndexes[ch] = index; }else if (cIndexes[ch] >= 0){ cIndexes[ch] = -2; } index ++; } //return the first appearence once char in current stringstream public char FirstAppearingOnce() { //遍历每一个出现一次的字符,将index最小的值返回 int min = Integer.MAX_VALUE; char minC = '#'; for (int i = 0; i < cIndexes.length; i++){ if (cIndexes[i] >= 0 && cIndexes[i] < min){ min = cIndexes[i]; minC = (char) i; } } return minC; }}
法二:
public class FirstApperanceOnce { ArrayList<Character> list = new ArrayList<Character>(); int[] counts = new int[128]; //Insert one char from stringstream public void Insert(char ch) { if(counts[(int)ch] ==0){ list.add(ch); }else if(list.contains(ch)){ list.remove((Character)ch); } counts[(int)ch] = counts[(int)ch]+1; } //return the first appearence once char in current stringstream public char firstAppearingOnce() { return list.isEmpty()?'#':list.get(0); }}
11、链表中环的入口结点
(1)问题描述:
一个链表中包含环,请找出该链表的环的入口结点。
(2)解题思路:
法一:使用ArrayList,遍历链表,将每个Node都存入List,如果list中存在该结点,则该节点为环中的入口。
法二:使用两个相邻的指针,断开每次的之前的节点,当前面的指针指向null时,后面的指针即为环的入口结点。
法三:快指针与慢指针。
(3)代码实现:
法一:
public Node solutionWithList(Node pHead){ ArrayList <Node> list=new ArrayList<Node>(); list.add(pHead); Node curnode=pHead.next; if(curnode==null){ return null; } while(!list.contains(curnode)){ list.add(curnode); curnode=curnode.next; } return curnode; }
法二:
public Node EntryNodeOfLoop(Node pHead) { if(pHead.next == null){ return null; } Node front = pHead.next; Node behind = pHead; while (front != null){ behind.next = null; behind = front; front = front.next; } return behind; }
法三:
public ListNode EntryNodeOfLoop(ListNode pHead) { ListNode fast = pHead; ListNode slow = pHead; while(fast != null && fast.next != null){ fast = fast.next.next; slow = slow.next; //当快指针 与 慢指针相遇时 if(fast == slow){ fast = pHead; //再次相遇 while(fast != slow){ fast = fast.next; slow = slow.next; } return fast; } } return null; }
- 剑指Offer编程整理(五)
- 剑指offer编程整理(一)
- 剑指Offer编程整理(二)
- 剑指Offer编程整理(三)
- 剑指Offer编程整理(四)
- 剑指Offer编程整理(六)
- 剑指offer之编程(五)
- 剑指offer经典编程(五)
- 剑指offer(五)
- 剑指offer整理归纳(1/2)
- 剑指offer整理归纳(2/2)
- 《剑指offer》python答案整理(1)
- 剑指offer-算法整理
- 剑指offer 练习五(Java版)
- 剑指offer 五
- 剑指offer题五
- Attribute在.NET编程中的应用整理(五)
- 剑指offer部分题目整理
- 阿里云部署(一):注册、备案
- 多线程的一些知识点
- LeetCode: 421. Maximum XOR of Two Numbers in an Array
- Hadoop概述
- linux内核字符设备驱动之读操作
- 剑指Offer编程整理(五)
- Linux进阶之 find命令之exec
- 领导给我一个指令文档,写出一个调用加密机的函数,想哭
- Java学习笔记
- 四轴飞行器的相关知识
- ios-Swift中的懒加载和getter、setter方法
- android 关机流程解析
- linux常见压缩解压命令
- Hive常见问题及处理方法