CC150附加题

本文提供了一系列算法题目及解答,包括数组操作、字符串处理、数学计算、树结构转换等内容,旨在帮助读者提高算法水平。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

中等难度题

17.1编写一个函数,不使用临时变量,直接交换两个数
加减法
public void change(int a, int b){
    a = a - b;
    b = b + a;
    a = b - a;
}
位操作
public void change(int a, int b){
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
} 
17.2检查一个人是否完成了井字游戏

思路: 现在红色3 * 3 的棋盘,存在两种设计思路,
1。直接对每一个空格进行编码 3的0次方v0, 3的1次方v1 ,3的2次方v2。。。一直到3的8次方v8,其中 vi为0, 表示没有这个数, vi为1,表示红方在,vi为2,表示蓝方在, 对结果进行缓存
2.检查行,检查列,检查对角线,检查逆对角线

17.3设计一个算法,计算出n阶乘有多少个尾随0
//这个尤其要注意, 25是包含两个5的
public int countFactZeros(int num){
    if (num < 5){
        return 0;
    }
    int count = 0;
    for (int i = 5 ; num / i > 0; i *= 5){
        res += num / i;
    }
    return count;
}
17.4编写一个方法,找出两个数中最大的那一个。不得使用if或其他运算比较符。

思路:利用位运算,对大的数乘以1,小的数乘以0

public int sign(int num){
    return !((num >> 31) & 0x01);
}
public int flip(int num){
    return 1 ^ num; 
}
public int getBigger(int a, int b){
    int c = a - b;
    int sa = sign(a);//这个函数四个很关键的函数,大于等于0 返回1,小于0 返回0,flip反转也对
    int sb = sign(b);
    int sc = sign(c);// a - b 的符号
    int use_sign_of_a = sa ^ sb;
    int use_sign_of_c = flip (sa ^ sb);
    int k = use_sign_of_a * a + use_sign_of_c * c;
    int p = flip(k);
    return a * k + b * p;
}
17.5珠玑妙算游戏,R,Y,G,B 。实际组合为RGBY,猜测GGRR返回,一次猜中,一次伪猜中。
    public static void  getResult(String real, String guess){
        char[] ra = real.toCharArray();
        char[] ga = guess.toCharArray();
        int real1 = 0;
        int sreal = 0;
        int index = 0;
        int [] temp = new int[4];
        for (char c : ga){
            if (c == ra[index++]){
                real1++;
            } else{
                int code = getCode(c);// 这个地方加上去的是solution的解
                temp[code]++;
            }
        }
        for (int i  = 0; i < ga.length; i++){
            index = getCode(ga[i]);
            if (temp[index] > 0 && ra[i] != ga[i]){
                temp[index]--;
                sreal++;
            }
        }
        System.out.println(real1);
        System.out.println(sreal);
    }
    public static int getCode(char c){
        switch (c){
            case 'B':
                return 0;
            case 'G':
                return 1;
            case 'R':
                return 2;
            case 'Y':
                return 3;
            default :
                return -1;              
        }
    }
17.6给定一个整数数组,编写一个函数,找出索引m和n,只要将m和n之间的元素排好序,整个数组就是有序的。注意 n - m越小越好。

思路:左增长序列索引,右增长数列索引,中间夹杂的数列的最小值与左边对比,最大值与右边对比

17.7给定一个整数,打印该整数的英文描述(例如“One Thousand, Two Hundred Thirty Four”)

思路: 熟悉正常的读法,然后构造读百位的方法,然后填充三位数的单位
注意 String[] 数组要使用 static关键字进行修饰

    public static String[] digits = {"One", "Two" , "Three" , "Four" , "Five", "Six", "Seven", "Eight", "Nine"};
    public static String[] teens = {"Eleven", "Twelve", "Thirten", "Fourteen","Fifteen","Sixteen", "Seventeen", "Eighteen", "Nineteen"};
    public static String[] tens = {"Ten", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"};
    public static String[] bigs = {"", "Thousands", "Million", "Billion"};
    public static  String numToString100(int num){
        StringBuffer sb = new StringBuffer();
        if (num > 100){
            sb.append(digits[num / 100 - 1] + " Hundred ");
            num %= 100;
        }
        if (num >= 11 && num <=19){
            sb.append(teens[num - 11] + " ");
            return sb.toString();
        } else if (num == 10 || num >= 20){
            sb.append(tens[num / 10 - 1] + " ");
            num %= 10;
        }
        if (num > 0 && num <= 9){
            sb.append(digits[num - 1] + " ");   
        }
        return sb.toString();
    }
    public static String numToString(int num){
        if (num == 0){
            return "Zero";
        }
        if (num < 0){
            return "Negative " + numToString(-1 * num);
        }
        int count = 0;
        String res = "";
        while (num > 0){
            if (num % 1000 != 0){
                res = numToString100(num % 1000) +  bigs[count] + " "  + res;
            }
            count++;
            num /= 1000;
        }
        return res;
    }
17.8给定一个数组(有正数有负数),找出总和最大的连续数列,并返回总和。

思路:这个题有点DP的意思,需要缓存每一个加到现在的数,用模式匹配的方法,与leetcode的买股票类似

public static int getMaxSum(int[] nums){
    int sum = 0;
    int maxsum = 0;
    for (int i = 0; i < nums.length; i++){
        sum += nums[i];
        if (sum > maxsum){
            maxsum = sum;
        } else if (sum < 0){
            sum = 0;
        }
    }
    return maxsum; 
}
17.10 XML非常冗长,你可将每个标签对应为预先定义好的整数值,encode
17.11给定rand5(),实现一个方法rand7()。也即,给定一个产生0到4(含)随机数方法,编写一个产生0到6(含)随机数的方法。

rand5随机产生0-4 的数,rand7随机产生0 -6的数,提供两种思路,但是次数都不定
思路1: 5 * rand5() + rand5(); 随机产生0 -24 的数,只选取其中的0 -20,这个地方要注意了,之前考虑的出错了。
思路2: 2 * rand5() 获得 0 - 9 之间的偶数, 舍弃rand5()等于4的时候,对2求余得到 0.5的概率,加到第一个数上去。

17.12设计一个算法,找出数组中两数之和为指定值的所有整数对。

思路:1.O(n)时间复杂度,将数组中的数都放到map中, 然后半边迭代,如果map中包含num - x,输出
2.如果是有序数组,两个指针,一个在开始,一个在结束,left < right 的时候进行处理,和与目标值相等:left++,right–;否则如果小 left ++ ,如果大right–,重复元素不好解决。

17.13有个简单的类似结点的数据结构BinNode,包含两个指向其他结点的指针,将查找二叉树转化为双链表的形式。

这个题目可以看出来,递归真是大法宝,归并排序的方式类似,解决好当前点的处理模式

// 因为二叉查找树的中序遍历就是增序的访问,这个是非递归的形式
    public BinNode changeTreeToList(BinNode root){
        if (null == root){
            return null;
        }
        BinNode head= new BinNode();
        BinNode temp = root;
        BinNode temp2 = head;
        Stack<BinNode> stack = new Stack<BinNode>();
        while(true){
            if (temp != null){
                stack.push(temp);
                temp = temp.node1;//left, before
            } else if (stack.size() > 0){
                temp = stack.pop();
                BinNode node = new BinNode(temp.val);
                temp2.node2 = node;
                node.node1 = temp2 ;
                temp2 = node;
                temp = temp.node2;//right , after
            } else {
                break;
            }
        }
        head = head.node2;
        head.node1 = null;
        return head;
    }
// 递归的形式
    public NodePair convert(BinNode root){
        if (root == null){
            return null;
        }
        NodePair left = convert(root.node1);// left
        NodePair right = convert(root.node2);// right
        if (left != null){
            contact(left.tail, root);
        }
        if (right != null){
            contact(root, right.head);
        }
        return new NodePair(left == null ? root : left.head, right == null ? root : right.tail);
    }
    public void contact(BinNode no1, BinNode no2){
        no1.node2 = no2;
        no2.node1 = no1;
    }
    public class NodePair{
        BinNode head;
        BinNode tail;
        public NodePair(BinNode he, BinNode tai){
            head = he;
            tail = tai;
        }
    }
17.14给你字符串,给你字典,解析成正确的句子。

这本书上给的动态规划的策略是对递归加传入参数的缓存。
在返回值里面 如果包含多个返回值,新建一个包裹类。
在DP里面,如果缓存的是对象而不是基本的数据类型,很可能需要复制该对象。

高难度题

18.1编写函数实现两个数相加。不得使用加号或其他运算符。

这边涉及到了加法的原理

public int add(int a, int b){
    if (b == 0){
        return a;   
    }
    int sum = a ^ b;
    int carry = a & b << 1;
    return add(sum, carry);
}
// 这里给出leetcode的最优解,一步只加减,不进位,一步考虑进位,如果两者相与为0,则可直接进行加减数
public int add (int a, int b) {
    int x = 0, y = 0;
    while (a & b != 0) {
        x = a ^ b;// add 
        y = a & b << 1;//carry
        a = x;
        b = y;
    }
    return a | b;
}
18.2编写一个方法,洗一副牌。要求做到完美洗牌。给定一个完美的随机数发生器。
//递归方法
int rand(int lower, int higher) {
    return lower + (int)(Math.random() * (higher - lower + 1));
}
public int[] shuffleArray(int[] cards, int i){
    if (i == 0){
        return cards;
    }
    // 
    shuffleArray(cards, i - 1);
    int k = rand(0, i);
    int temp = cards[i];
    cards[i] = cards[k];
    cards[k] = temp;
    return cards;
}
//迭代也是从小到大交换即可。
18.3编写一个方法,从大小为n的数组中随机选出m个整数。要求每个元素被选中的概率相同。

思路:与上题目类似,只不过是先进行了m的填充。,这个概率,不要忘记初始的问题

18.4数出0到n(含)中数字2出现了几次。

思路: 1.暴力求解, 先如此,后叠加
2. 找数字中出现的规则

    public static int count2sDigit(int num, int d){
        int powerof10 = (int)Math.pow(10, d);
        int nextpowerof10 = powerof10 * 10;
        int right = num % powerof10;
        int roundDown = num - num % nextpowerof10;
        int roundUp = roundDown +  nextpowerof10;
        int digit = (num / powerof10) % 10;
        if (digit < 2){
            return roundDown / 10;
        } else if (digit == 2){
            return roundDown / 10 + right + 1;
        } else {
            return roundUp / 10;
        }
    }
    public static int count2sInRange(int number){
        if (number < 0){
            return count2sInRange(-number);
        }
        String num = String.valueOf(number);
        int count = 0;
        for (int i = 0; i < num.length(); i++){
            count += count2sDigit(number , i);
        }
        return count;
    }
18.6 给定10亿个数字,找出最小的100万个数字。假定计算机内存足以容纳全部10亿个数字。

思路: 1.TreeSet
2.排序 时间复杂度 O(nlog(n));
3.大顶堆
4.选择排序法(快排),可在预期的O(n)时间内找到第i个最小的元素。

18.7给定一组单词,找出其中最长单词,且该单词由这组单词中的其他单词组合而成
public class LengthComparator implements Comparator<String> {
    @Override
    public int compare(String o1, String o2) {
        if (o1.length() >= o2.length()){
            return -1;
        } else {
            return 1;
        }
    }// 按照长度从大到小进行排列
}
// 递归实现字符串能否被构建
public static boolean canBuild(String str, boolean flag, Map<String, Boolean> map){
        if (map.containsKey(str) && !flag){
            return map.get(str);
        }
        for (int i = 1; i < str.length(); i++){
            String left = str.substring(0, i);
            String right = str.substring(i);
            if (map.containsKey(left) && map.get(left) == true && canBuild(right, false, map)){
                return true; 
            }
        }
        map.put(str, false);
        return false;
    }
public static String getLong (String[] strs){
        if (null == strs || strs.length < 1 ){
            return null;
        }
        Map<String, Boolean> map = new HashMap<String, Boolean>();
        Arrays.sort(strs, new LengthComparator());
        for (String s : strs){
            map.put(s, true);
            System.out.println(s);
        }
        for (String s : strs){
            if (canBuild(s, true, map)){
                return s;
            }
        }
        return "";
}
18.8给定一个字符串s和一个包含短字符串的数组T,设计一个方法,根据T中的每一个较短的字符串,对s进行搜索。

思路: 新的数据结构,后缀树, 敲黑板。考试必考重点题型,重点。

//后缀树的构建和搜索,短字符串如果出现,返回的是index
//每个字符后面跟的子树的index都是第一个子节点的index,如果第一代儿子的有两个或者两个以上的子树,indexes就会存在多个
public class SuffixTree {
    SuffixTreeNode root = new SuffixTreeNode();
    public SuffixTree(String s){
        for (int i = 0; i < s.length(); i++){
            String suffix = s.substring(i);
            root.insertString(suffix, i);
        }
    }
    public ArrayList<Integer> search(String s){
        return root.search(s);
    }
}
public class SuffixTreeNode {
    HashMap<Character, SuffixTreeNode> children = new HashMap<Character, SuffixTreeNode>();
    char value;
    ArrayList<Integer> indexes = new ArrayList<Integer>();
    public SuffixTreeNode(){}
    public void insertString(String s, int index){
        indexes.add(index);
        if (s != null && s.length() > 0){
            value = s.charAt(0);
            SuffixTreeNode child = null;
            if (children.containsKey(value)){
                child = children.get(value);
            } else {
                child = new SuffixTreeNode();
                children.put(value, child); 
            }
            String remainder = s.substring(1);
            child.insertString(remainder, index);
        }
    }
    public ArrayList<Integer> search(String s){
        if (s == null || s.length() == 0){
            return indexes;
        }  else {
            char first = s.charAt(0);
            if (children.containsKey(first)){
                String remainder = s.substring(1);
                return children.get(first).search(remainder);
            }
        }
        return null;
    }
}
18.9随机生成一些树并传入,每当收到新的数字时,找出并标记中位数。

思路:利用两个优先级堆(pariority):一个大顶堆,存放小于中位数的值,以及一个小顶堆,存放大于中位数的值。
这两个堆实现了比较完备的平衡

//tips x > y 返回-1 ,生成的队列是从大到小排列的。 默认队列是小的数在队列前面,也就是小堆顶
public class MyPriorityQueue {
    private PriorityQueue<Integer> minHeap = null;
    private PriorityQueue<Integer> maxHeap = null;
    public MyPriorityQueue(){
        Comparator<Integer> maxHeapComparator = new maxHeapComparator();
        minHeap = new PriorityQueue<Integer>();
        maxHeap = new PriorityQueue<Integer>(11, maxHeapComparator);
    }
    public void addNewNumber(int num){
        if (minHeap.size() == maxHeap.size()){// max num > = min num,条件得以保证
            if (minHeap.peek() != null && num > minHeap.peek()){
                maxHeap.add(minHeap.poll());
                minHeap.add(num);
            } else {
                maxHeap.offer(num);// max size > min size,初始两者的size是保持相等的
            }
        } else {
            if (num < maxHeap.peek()){
                minHeap.add(maxHeap.poll());
                maxHeap.add(num);
            } else {
                minHeap.add(num);//此时 持平
            }
        }
    }
    public double getMeidan(){
        if (maxHeap.isEmpty()){
            return 0;
        }
        if (maxHeap.size() == minHeap.size()){
            return (double)(maxHeap.peek() + minHeap.peek()) / 2.0;
        } else {
            return maxHeap.peek();
        }
    }
18.10给定两个字典里的单词,长度相等。编写一个方法,将一个单词变化成另一个单词。一次只改动一个单词。在变换的过程中,每一步得到的新单词都必须是字典里存在的。

思路:这里面用到了图的知识,广度优先搜索。深度搜索和广度搜索,也就是深搜与广搜。
这个地方假设字符串的长度n,看一下单词的数量,构造图的时候,获取临近节点的位置,看下字典中单词的数量,多的话用 24的n次方,少的话,用遍历字典,广度优先得到的是最短路径。
这边采用遍历字典的方式,
边构建图,边记录回溯路径
单向图
没有bugfree的原因是把t写成了w
用了HashMap缓存了路径

public class Graph {
    public Set<String> getAdjacent(String s, Set<String> dict){
        Set<String> set = new HashSet<String>();
        for (String single : dict){
            if (single.length() == s.length()){
                int count = 0;
                for (int i = 0; i < s.length(); i++){
                    if (s.charAt(i) != single.charAt(i)){
                        count++;
                    }
                }
                if (count == 1){
                    set.add(single);
                }
            }
        }
        return set;
    }
    public LinkedList<String> transForm(String start, String end, Set<String> dictionary){
        if (start == null || end == null){
            return null;
        }
        Queue<String> qu = new LinkedList<String>();
        Set<String> visited = new HashSet<String>();  
        Map<String, String> trackpath = new TreeMap<String, String>();
        qu.add(start);
        visited.add(start);
        while(!qu.isEmpty()){
            String w = qu.poll();
            Set<String> temp = getAdjacent(w, dictionary);
            for (String t : temp){
                if (t.equals(end)){
                    LinkedList<String> path = new LinkedList<String>();
                    path.add(t);
                    while(w != null){
                        path.add(w);
                        t = w;
                        w = trackpath.get(t);
                    }
                    return path;
                }
                if (!visited.contains(t)){
                    qu.add(t);
                    visited.add(t);
                    trackpath.put(t, w);
                }
            }
        }
        return null;
    }
}
18.11给定一个方阵,其中每个单元(像素)非黑即白。设计一个算法,找出四条边皆为黑色像素的最大子方阵。

思路:正常按照从大到小的边的长度进行迭代,O(n的四次方)。
预处理的话,就可以吧时间复杂度降为O(n的三次方)。新建一个包装类,预处理的方式为每一个点记录下,右边第几个为0或者边界,下面第几个为0或者边界。

18.12给定一个正整数和负整数组成的N*N矩阵,编写代码找出总和最大的子矩阵

思路:1.蛮力法:O(n的6次方)
因为不一定是方阵,所以要迭代O(n的4次方)个子矩阵,计算每个子矩阵的用时O(n的2次方),
2.动态规划:O(n的4次方),也不算动态规划
先进行预处理,每一个都计算其左上角矩阵值得大小
3.还有一个优化算法,时间复杂度为O(n的3次方)
此题参考一位数组,找出和最大的连续序列。

//这个特别巧妙的是利用了上一次迭代的过程
    public void clearArray(int[] array){
        for (int i = 0; i < array.length; i++){
            array[i] = 0;
        }
    }
    public int maxSubArray(int[] arr, int n){
        int max = Integer.MIN_VALUE;
        int sum = 0;
        for (int i = 0; i < n; i++){
            sum += arr[i];
            max = Math.max(max, sum);
            sum = Math.max(sum, 0);
        }
        return max;
    }
    public int maxSubMatrix(int[][] matrix){
        int row = matrix.length;
        int col = matrix[0].length;
        int[] partilSum = new int[col];
        int max = Integer.MIN_VALUE;
        int x = 0;
        int y = 0;
        for (int rowb = 0; rowb < row; rowb++){
            clearArray(partilSum);
            for (int rowe = rowb; rowe < row; rowe++){
                for (int co = 0; co < col; co++){
                    partilSum[co] += matrix[rowe][co];
                }
                for (int a : partilSum){
                }
                int runmax = maxSubArray(partilSum, col);
                if (runmax > max){
                    max = runmax;
                    x = rowb;
                    y = rowe;
                }
            }   
        }
        return max;
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值