ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

算法训练题笔记

2021-06-14 14:36:08  阅读:195  来源: 互联网

标签:paths 训练 int str2 i2 length 笔记 i1 算法


【题目】

给定一个路径数组 paths,表示一张图。paths[i]==j 代表城市 i 连向城市 j,如果 paths[i]==i, 则表示 i 城市是首都,一张图里只会有一个首都且图中除首都指向自己之 外不会有环。 例如, paths=[9,1,4,9,0,4,8,9,0,1], 由数组表示的图可以知道,城市 1 是首都,所以距离为 0,离首都距离为 1 的城市只有城 市 9,离首都距离为 2 的城市有城市 0、3 和 7,离首都距离为 3 的城市有城市 4 和 8, 离首都 距离为 4 的城市有城市 2、5 和 6。所以距离为 0 的城市有 1 座,距离为 1 的 城市有 1 座,距离 为 2 的城市有 3 座,距离为 3 的城市有 2 座,距离为 4 的城市有 3 座。

那么统计数组为nums=[1,1,3,2,3,0,0,0,0,0],nums[i]==j 代表距离为 i 的城市有 j 座。

要求实现一个 void 类型的函 数,输入一个路径数组 paths,直接在原数组上调整, 使之变为 nums 数组,即 paths=[9,1,4,9,0,4,8,9,0,1]经过这个函数处理后变成 [1,1,3,2,3,0,0,0,0,0]。

【要求】 如果 paths 长度为 N,请达到时间复杂度为 O(N),额外空间复杂度为 O(1)。

【思路】

 

思路:本题非常复杂。

 

直接在原数据上进行修改,使用start,next,last三个指针。

 

start记录起始城市,next记录下标i连接的path[i]城市序号,last表示之前的一个城市序号,从0位置开始,只要还没找到首都就不断以path[i]作为下标去寻找,然后记下跳转了多少次,以负数的形式将跳转数写进path[i],因为负数可以表示当前城市到首都的路程已经被遍历过。然后就能形成一个每个位置到首都的距离的数组。

 

在此数组上继续执行下一个流程统计每个距离的城市数量。比如当前0位置,path[0] = -3,证明0距离首都的距离是3,先将3位置对应path值记下,再将3位置对应path置为1,

 

往下顺序重复执行如上操作。即可得到nums数组。

【Code】

 

public static void pathsToNums(int[] paths) {
        if (paths == null || paths.length == 0) {
            return;
        }
        // citiesPath -> distancesArray
        pathsToDistans(paths);

        // distancesArray -> numArray
        distansToNums(paths);
    }

    public static void pathsToDistans(int[] paths) {
        int cap = 0;
        for(int i = 0;i !=paths.length;i++)
        {
            if(path[i] == i)
                cap = i;
            else if(path[i] > -1)//当前城市到首都的路径还没被遍历过
            {
                int curI = paths[i];
                paths[i] = -1;
                int preI = i;
                while(path[curI] != curI)//如果还没找到首都一直找下去
                {
                    if(path[curI] > -1)//找路径未被遍历过的
                    {
                        int nextI = paths[curI];
                        paths[curI] = preI;
                        preI = curI;
                        curI = nextI;                        
                    }
                    else 
                        break;
                }
                int value = paths[curI] == curI ? 0:paths[curI];
                //顺着路上存的线索一步步回退
                while(paths[curI] != -1)
                {
                    int lastPreI = paths[preI];//下一个线索
                    paths[preI] = --value;//当前preI对应的path[]里的线索已经用了,所以置为到首都的距离
                    curI = preI;//当前指针
                    preI = lastPreI;//继续回退pre指针
                }
                paths[preI] = --value;
            }
        }
        paths[cap] =0; // 首都位置做特别处理
    }

    public static void distansToNums(int[] disArr) {
        for(int i = 0;i != disArr.length;i++)
        {
            int index = disArr[i];
            if(index < 0)//证明还未统计过距离首都i长度的城市数量
            {
                disArr[i] = 0;
                while(true)
                {
                    index = -index;
                    if(disArr[index] > -1)
                    {
                        disArr[index]++;
                        break;
                    }
                    else{
                        int nextIndex = disArr[index];
                        disArr[index] = i;
                        index = nextIndex;
                    }
                }
            }
        }
}

 

【题目】

给定两个有序数组arr1和arr2,再给定一个整数k,返回来自arr1和arr2的两个数相加和最 大的前k个,两个数必须分别来自两个数组。 【举例】 arr1=[1,2,3,4,5],arr2=[3,5,7,9,11],k=4。 返回数组[16,15,14,14] 【要求】 时间复杂度达到 O(klogk)

【思路】

1、设计一个新的对象结构,包含两个下标和两个下标在数组中对应的数相加的值

2、设计一个大根堆结构,按照该对象包含的值元素进行排序

3、先将两个数组的length-1下标与对应值包装成对象丢进堆里(这个对象包含的值一定是答案中最大的)

4、再出堆顶对象,丢进该对象的(length-1,length-2)和(length-2,length-1)

5、循环直到出堆顶了K个对象结束

魔改题:

一个二维数组,每行和每一列都有序,但整体无序,求数组中的前k个最小值

思路:与上面一样,只不过此时找最小值,起点为(0,0),加入小根堆,然后把(0,1),(1,0)加入小根堆,再根据哪个点大,将该点下方和右方的点值加入小根堆,直至出了k个元素就可以了。

【Code】

 

public static class Node
    {
        public int index1;//arr1中的位置
        public int index2;//arr2中的位置
        public int value;//arr1[index1] + arr2[index2] 的值

        public Node(int i1,int i2,int sum)
        {
            index1 = i1;
            index2 = i2;
            value = sum;
        }
    }

    //生成大根堆的比较器
    public static class MaxHeapComp implements Comparator<Node>
    {
        @Override
        public int compare(Node o1,Node o2)
        {
            return o2.value - o1.value;
        }
    }

    public static int[] topKSum(int[] arr1,int[] arr2,int topK)
    {
        if(arr1 == null || arr2 == null || topK < 1)
        {
            return null;
        }
        topK = Math.min(topK, arr1.length * arr2.length);
        int[] res = new int[topK];
        int resIndex = 0;//已经出堆的个数
        PriorityQueue<Node> maxHeap = new PriorityQueue<>(new MaxHeapComp());
        HashSet<String> positionSet = new HashSet<String>();
        int i1 = arr1.length-1;
        int i2 = arr2.length-1;
        maxHeap.add(new Node(i1, i2, arr[i1]+arr[i2]));//先将右下角位置丢进堆
        positionSet.add(String.valueOf(i1+"_"+i2));
        while(resIndex!=topK)
        {
            Node curNode = maxHeap.poll();
            res[resIndex++] = curNode.value;
            i1 = curNode.index1;
            i2 = curNode.index2;
            //先检测该位置是否已被丢进过,不重复丢进堆里
            if(!positionSet.contains(String.valueOf((i1-1) + "_" + i2)))
            {
                positionSet.add(String.valueOf((i1-1) + "_" + i2));
                maxHeap.add(new Node(i1-1, i2, arr1[i1-1] + arr2[i2]));
            }
            if(!positionSet.contains(String.valueOf(i1 + "_" + (i2-1))))
            {
                positionSet.add(String.valueOf(i1 + "_" +(i2-1)));
                maxHeap.add(new Node(i1, i2-1, arr[i1] + arr2[i2-1]));
            }
        }
        return res;
    }

 

 

【题目】

给定三个字符串str1、str2和aim,如果aim包含且仅包含来自str1和str2的所有字符, 而且在aim中属于str1的字符之间保持原来在str1中的顺序,属于str2的字符之间保持 原来在str2中的顺序,那么称aim是str1和str2的交错组成。实现一个函数,判断aim是 否是str1和str2交错组成 【举例】 str1="AB",str2="12"。那么"AB12"、"A1B2"、"A12B"、"1A2B"和"1AB2"等都是 str1 和 str2 的 交错组成。

【思路】

动态规划:构建一张二维表dp,dp[i][j]代表以str1从0~i的字串 + str2从0~j的字串是否能交错组合成 aim从0~i+j的字串。这张表的右下角位置dp[str1.length-1][str2.length-1]就是答案。

常规位置的求法:求两种情况其中之一为true就true

1、aim子串以str1 i-1位置结尾并且dp[i-1][j]为true

aim子串以str2 j-1位置结尾并且dp[i][j-1]为true

 

【Code】

 

public static boolean isCross(String str1,String str2,String aim)
    {
        if(str1 == null || str2 == null || aim == null)
            return false;
        char[] ch1 = str1.toCharArray();
        char[] ch2 = str2.toCharArray();
        char[] chaim =  aim.toCharArray();
        if(chaim.length != ch1.length + ch2.length)
            return false;
        //构建二维表,预留一个位置0作为当一个str为0时另一个str能单独组成aim的值
        boolean[][] dp = new boolean[ch1.length+1][ch2.length+1];
        dp[0][0] = true;//str1和str2都是"".aim以0截止是"" 自然一样

        //分别构建第一行和第一列
        for(int i = 1;i <= ch1.length;i++)
        {
            if(ch1[i-1] != chaim[i-1])//如果不相等则不用往后比对了
                break;
            dp[i][0] = true;
        }
        for(int j = 1;j<ch2.length;j++)
        {
            if(ch2[j-1] != chaim[j-1])
                break;
            dp[j][0] = true;
        }
        for(int i = 1;i <= ch1.length;i++)
        {
            for(int j = 1;j<ch2.length;j++)
            {
                if( (ch1[i-1] == chaim[i+j-1] && dp[i-1][j])
                || (ch2[j-1] == chaim[i+j-1] && dp[i][j-1]))
                {
                    dp[i][j] = true;
                }
            }
        }
        return dp[ch1.length][ch2.length];
    }

 

标签:paths,训练,int,str2,i2,length,笔记,i1,算法
来源: https://www.cnblogs.com/pionice/p/14882384.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有