牛客几个有意思的小题

感觉以下几个小问题有点意思,有些代码是自己写的,有些则是总结别人或者引用别人的,代码可能有些不足或者其他解法以及优化,欢迎指出,一起进步。

   /**
    * 问题描述:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
    * 思路:使用set,如果集合中存在该元素则删除,否则加入,最后元素只会剩下只出现一次的元素,得到结果。
    */
   public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
       Set<Integer> set = new HashSet<Integer>();
       for(int i = 0; i < array.length; i++){
           //包含则删除
           if (set.contains(array[i])){
               set.remove(array[i]);
           }else {
               set.add(array[i]);
           }
       }
       Integer[] temp = new Integer[2];
       set.toArray(temp);
       num1[0] = temp[0];
       num2[0] = temp[1];
   }
    /**
     * 获得两个整形二进制表达位数不同的数量。
     * @param m 整数m
     * @param n 整数n
     * @return 整型
     */
    public int countBitDiff(int m, int n){
        //先进行异或处理,然后0替换掉,判断长度
        String string = Integer.toBinaryString(m^n);
        string = string.replace("0", "");
        return string.length();
    }
    /**
     * 输入两个链表,找出它们的第一个公共结点。
     * 思路:遍历第一个链表并将值保存在map中,其次遍历第二条链表,判断该值是否存在map中,存在则返回。
     * @param pHead1
     * @param pHead2
     * @return ListNode
     */
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode currentOne = pHead1;
        ListNode currentTwo = pHead2;
        Map<ListNode, Integer> map = new HashMap<ListNode, Integer>();
        while (currentOne != null){
            //存入map
            map.put(currentOne, null);
            currentOne = currentOne.next;
        }
        while (currentTwo != null){
            //存在则返回第一个节点
            if (map.containsKey(currentTwo)){
                return currentTwo;
            }else {
                currentTwo = currentTwo.next;
            }
        }
        return null;
    }
    /**
     * 问题描述:假设一个球从任意高度自由落下,每次落地后反跳回原高度的一半;
     * 再落下, 求它在第5次落地时,共经历多少米?第5次反弹多高?
     */
    public static void getJourney(int high)
    {
        double back = high;
        double sum = high;
        for(int i = 0; i < 5; i++){
            sum += back;
            back = back/2;
        }
        System.out.println(sum);
        System.out.println(back);
    }
    /**
     * 将一个字符中所有出现的数字前后加上符号“*”,其他字符保持不变。
     * 思路:在数字前后都加上*,最后将**替换掉。(看牛友的,感觉思路很棒)
     */
    public static String MarkNum(String pInStr)
    {
        StringBuffer stringBuffer = new StringBuffer();
        for(int i = 0; i < pInStr.length(); i++){
            if (pInStr.charAt(i) >= 48 && pInStr.charAt(i) <= 57){
                stringBuffer.append("*" + pInStr.charAt(i) + "*");
            }else {
                stringBuffer.append(pInStr.charAt(i));
            }
        }
        System.out.println(stringBuffer.toString());
        return stringBuffer.toString().replace("**", "");
    }
    /**
     * 找出字符串中第一个只出现一次的字符,
     * 输出第一个只出现一次的字符,如果不存在输出-1。
     * 思路:使用map将字符作为key,出现次数作为value,很多字符串的题都可以这样做。
     */
    public static void PrintSingleChar(String string){
        Map<Character, Integer> map = new HashMap<Character, Integer>();
        for (int i = 0; i < string.length(); i++){
            if (map.containsKey(string.charAt(i))){
                //存在则次数加1
                map.put(string.charAt(i), map.get(string.charAt(i))+1);
            }else {
                //不存在则value=1
                map.put(string.charAt(i), 1);
            }
        }
        for (int i = 0; i < string.length(); i++){
            if (map.get(string.charAt(i)) == 1){
                System.out.println(string.charAt(i));
                return;
            }
        }
        System.out.println("-1");
    }
    /**
     * 输入一个链表,输出该链表中倒数第k个结点。
     * 思路:存入栈中,弹出k-1个数后返回k个。
     * 注意:栈不能为空,栈长度大等于k,k大于0,否则都返回null。
     */
    public ListNode FindKthToTail(ListNode head,int k) {
        ListNode temp = head;
        Stack<ListNode> stack = new Stack<ListNode>();
        //直接遍历入栈
        while (temp != null){
            stack.push(temp);
            temp = temp.next;
        }
        if (!stack.isEmpty() && stack.size() >= k && k > 0){
            for (int i = k-1; i > 0; i--){
                stack.pop();
            }
            return stack.pop();
        }else {
            return null;
        }
    }
    /**
     * 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?
     * 为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,
     * 但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,
     * 可以很快的求出任意非负整数区间中1出现的次数。
     * 思路:转存字符串数组,遍历。
     */
    public static int NumberOf1Between1AndN_Solution(int n) {
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i <= n; i++){
            stringBuffer.append(i);
        }
        int count = 0;
        for (int i = 0; i < stringBuffer.length(); i++){
            if(stringBuffer.charAt(i) == '1'){
                count++;
            }
        }
        return count;
    }
    /**
     * 统计一个数字在排序数组中出现的次数。
     * 思路:二分法(看到牛友的解答,感觉很棒),不过必须在顺序从小到大的情况下。
     */
    public static int GetNumberOfK(int [] array , int k) {
        int start = GetLower(array, k);
        int end = GetUper(array, k);
        return end - start + 1;
    }
    //获取k第一次出现的下标
    public static int GetLower(int [] array , int k){
        int start = 0;
        int end = array.length-1;
        int mid = (start + end)/2;
        while (start <= end){
            if (array[mid] < k){
                start = mid + 1;
            }else {
                end = mid - 1;
            }
            mid = (start + end)/2;
        }
        return start;
    }
    //获取k最后一次出现的下标
    public static int GetUper(int[] array, int k){
        int start = 0;
        int end = array.length-1;
        int mid = (start + end)/2;
        while (start <= end){
            if (array[mid] <= k){
                start = mid + 1;
            }else {
                end = mid - 1;
            }
            mid = (start + end)/2;
        }
        return end;
    }
    /**
     * 一个链表中包含环,请找出该链表的环的入口结点。
     * 思路:要寻找环的入口节点,遍历节点的时候,遇到的第一个重复节点肯定入环节点,
     * 所以定义一个Set,添加失败时即返回入口节点
     */
    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if (pHead == null){
            return null;
        }else {
            Set<Integer> set = new HashSet<Integer>();
            ListNode listNode = pHead;
            while (listNode != null){
                if(!set.add(listNode.val)){
                    return listNode;
                }
                listNode = listNode.next;
            }
            return null;
        }
    }
    /**
     * 汇编语言中有一种移位指令叫做循环左移(ROL),
     * 现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,
     * 请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,
     * 即“XYZdefabc”。是不是很简单?OK,搞定它!
     * @param str
     * @param n
     * @return
     */
    public static String LeftRotateString(String str,int n) {
        int count;
        //字符串为空
        if (str.length() == 0){
            return "";
        }
        //字符串不为空并且n大于字符串长度
        if (n > str.length()){
            count = n%str.length(); //循环str长度倍的话,顺序不变,所以取余即可
        }else {
            count = n;
        }
        return str.substring(count) + str.substring(0, count);
    }
    /**
     * 给定两个字符串,请编写程序,确定其中一个字符串的字符重新排列后,能否变成另一个字符串。
     * 这里规定大小写为不同字符,且考虑字符串重点空格。
     * 给定一个string stringA和一个string stringB,请返回一个bool,代表两串是否重新排列后可相同。
     * 保证两串的长度都小于等于5000。
     * 思路:所有字符总数为256,定义两个个256的数组,下标分别表示字符的ASCII值,对应数值表示出现次数,
     * 统计AB字符串后遍历两个数组比较对应位置数值是否相同即可。
     * @param stringA
     * @param stringB
     * @return
     */
    public boolean checkSam(String stringA, String stringB) {
        //两字符串存在空的情况
        if (stringA == null || stringB == null){
            return false;
        }
        /**
         * 两字符串都不为空
         * 长度不相等则直接返回false
         */
        if (stringA.length() != stringB.length()){
            return false;
        }
        int[] strA = new int[256];
        int[] strB = new int[256];
        for(int i = 0; i < stringA.length(); i++){
            strA[stringA.charAt(i)]++;
            strB[stringB.charAt(i)]++;
        }
        //遍历数组所有元素,进行对比
        for (int i = 0; i < 256; i++){
            if (strA[i] != strB[i]){
                return false;
            }
        }
        return true;
    }
    /**
     * 利用字符重复出现的次数,编写一个方法,实现基本的字符串压缩功能。
     * 比如,字符串“aabcccccaaa”经压缩会变成“a2b1c5a3”。若压缩后的字符串没有变短,则返回原先的字符串。
     * 给定一个string iniString为待压缩的串(长度小于等于10000),保证串内字符均由大小写英文字母组成,返回一个string,为所求的压缩后或未变化的串。
     * @param iniString
     * @return
     */
    public static String zipString(String iniString) {
        StringBuffer stringBuffer = new StringBuffer();
        int count = 1;
        for(int i = 0; i < iniString.length()-1; i++){
            if(iniString.charAt(i) == iniString.charAt(i+1)){
                count++;
            }else{
                stringBuffer.append(iniString.charAt(i));
                stringBuffer.append(count);
                count = 1;
            }
        }
        //System.out.println("stringBuffer = " + stringBuffer.toString());
        stringBuffer.append(iniString.charAt(iniString.length()-1));
        stringBuffer.append(count);
        //System.out.println("s.length = " + stringBuffer.length() + ", s = " + stringBuffer.toString());
        if(stringBuffer.length() >= iniString.length()){
            return iniString;
        }else{
            return stringBuffer.toString();
        }
    }
    /**
     * 请编写一个算法,若N阶方阵中某个元素为0,则将其所在的行与列清零。
     * 给定一个N阶方阵int[][](C++中为vector<vector><int>>)mat和矩阵的阶数n,
     * 请返回完成操作后的int[][]方阵,保证n小于等于300,矩阵中的元素为int范围内。
     */
    public int[][] clearZero(int[][] mat, int n) {
        Set<Integer> setX = new HashSet<Integer>();
        Set<Integer> setY = new HashSet<Integer>();
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                if(mat[i][j] == 0){
                    setX.add(i);
                    setY.add(j);
                }
            }
        }
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                if(setX.contains(i) || setY.contains(j)){
                    mat[i][j] = 0;
                }
            }
        }
        return mat;
    }

好好做题,努力搬砖,总有一天能娶得起媳妇儿,年轻人,别乱想。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值