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