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;
}