第一课刷题

题目目录:

1、给定一个整数n,判断该数是否是回文数

2、给定一个数组arr,和一个整数aim,请返回哪两个位置的数可以加出aim来。

3、按给定规律调整链表

4、把一棵搜索二叉树,转化成有序的双向链表

5、给定数组arr和整数num,共返回有多少个子数组满足如下情况:max(arr[i..j]) - min(arr[i..j]) <= num

【题目】

给定一个整数n,判断该数是否是回文数

【思路】

1、需要一个help变量=1

2、n的最高位

a)n/help,如果大于10,help*=10

b)n/help = 最高位

3、n的最低位

n%10

4、进行对比,如相等,则去除最高位和最低位,继续执行上述操作。

a)去除最高位:n=(n%help)

b)去除最低位:n/=10

c)Help/=100

【Code】

public static boolean isHuiWen(int n)
    {
        if(n<0)
            return false;
        int help = 1;
        while(n/help >= 10)
            help*=10;
        while(n!=0)
        {
            if(n/help != n%10)//对比最高位和末尾
                return false;
            n = (n%help) /10;//去除最高位和末尾
            help /=100;//同时去除两位,所以/10²
        }
        return true;
    }

【题目】

给定一个数组arr,和一个整数aim,请返回哪两个位置的数可以加出aim来。

例如:arr = {2, 7, 11, 15},target = 9 返回{0,1},因为arr[0] + arr[1] = 2 + 7 = 9

可以假设每个数组里只有一组答案

【思路】

1、暴力穷举

2、哈希表:时间复杂度O(N),空间复杂度O(N)

3、双指针:时间复杂度O(N*logN),空间复杂度O(n)

a) 对数组排序,在排序过程中记下原数据在原数组中的下标,形成数组

b) 左指针L指向0位置,右指针R指向N-1位置

c) L+R>target?R--:L++

d) c步骤的实质就是从两端进行尝试,根据数组有序的特性和指针的和的大小来判别哪段指针移动

表面上看是哈希表方法最优,哈希表虽增删查等操作都是O(1),但哈希函数的处理过程中产生的常数项很高。如果追求极致的话双指针方法较优。

【Code】

public static int[] twoSum(int[] nums,int target)
    {
        int[] indices = new int[nums.length];//存放排完序后的数据元素在原未排序数组中的下标
        //由于返回的需要是下标,且在排序过程中会丢失下标信息
        //因此需要将下标数组在排序过程中与数据一起变换
        for(int i = 0;i<indices.length;i++)
            indices[i] = i;
        sort(nums,indices);
        int l = 0;
        int r = nums.length-1;
        int sum = 0;
        while(l<r)
        {
            sum = nums[l]+nums[r];
            if(sum>target)
                r--;
            else if (sum<target) {
                l++;
            }
            else {
                return new int[] {indices[l],indices[r]};
            }
        }
        return new int[] {-1,-1};
    }

拓展:

1、如果给有序数组让你返回所有不重复的组的答案呢?

例如133557 target=8 明显答案为1-7、3-5

还是双指针的办法,只不过当指向3和5时,第一次找到该满足组,然后移动左指针到下一个不是当前指向的元素,即如果下一元素仍与上一指向元素相同,则跳过,直到不等

2、如果给target,要返回3个数之和等于target

对有序数组arr,从0位置元素a,对后面1-N-1找两数之和能够等于target-a的结果,与0位置组合,即为答案。如果后序有与上一位置相同的元素依旧执行跳过

 

【思路】

给定一个链表list,

如果:

list = 1 调整之后1。

list = 1->2 调整之后1->2

list = 1->2->3 调整之后1->2->3

list = 1->2->3->4 调整之后1->3->2->4

list = 1->2->3->4->5 调整之后1->3->2->4->5

list = 1->2->3->4->5->6 调整之后1->4->2->5->3->6

list = 1->2->3->4->5->6->7 调整之后1->4->2->5->3->6->7

根据上面的规律,调整一个任意长度的链表。

【思路】

规律:

当链表长度为偶数时,将整个链表以中间做为分割点划分为左右两部分,调整为左1-右1-左2-右2-...

当链表长度为奇数时,先无视最后一个数,进行偶数调整,再讲最后一个数追加进链表末端。

 

笔试写法:需要辅助空间

1、遍历链表,计算链表长度

2、开辟数组存放链表节点,长度为奇数时数组长度为奇数-1

3、遍历数组,将链表节点存进数组

4、创建help数组,遍历数组,如果i小于size/2,将help[i*2]=arr[i],即先将左边一半的数放入调整后的对应位置中。等左边一半的数放入完毕后,i来到右边,则将右边一半的数放入其对应位置中,位置关系为:help[ (i-(arr.length/2)) *2+1 ] = arr[i];

 

面试解法:不需要辅助空间,直接在链表进行调整然后返回

【Code】

 

【题目四】

把一棵搜索二叉树,转化成有序的双向链表。
【思路】
1、搜索信息然后整合
2、对根节点,期望左子树变成有序的双向链表,右子树也变成有序的双向链表,获得左子树的尾节点,右子树的头结点,将左子树的尾结点的right指向根,根的left指向尾结点,将右子树的头结点的left指向根,根的right指向头结点
3、遇到二叉树的题目想要用递归来搞,你想让左子树给你返回什么,右子树给你返回什么。收集信息然后再往上返回,让上头去从返回的信息当中选。
4、题解对左右子树要求 将树改为双向链表后返回链表的头结点和尾结点的节点组合。因为当要对根和左右子树进行连接时要用到左子树的尾结点和右子树的头结点,最终返回时又要返回左子树的头结点,需要的信息颇多,就这么做了,但其实还有一种方法不需要返回节点集合,只需要返回尾结点即可,就在链表整合完成后将尾结点的right指针指向头结点,再返回尾结点即可。上头拿到尾结点后,就可以根据尾结点.right找到头结点并拿出使用了。

【Code】

public static Node ChangeTree2DoubleLink(Node head)
    {
        if(head==null)
            return null;
        return process(head)[0];
    }
    //process功能:将head转化为双向链表并返回链表头结点,返回整个链表的头结点和尾节点
    public static Node[] process(Node head)
    {
        if(head==null)
            return null;
        //分别找左子树和右子树的头尾节点集合
        Node[] leftNode = process(head.letf);
        Node[] rightNode = process(head.right);
        head.letf = null;
        head.right= null;
        //连接之前检查是否为空。非空才连接
        if(leftNode[1]!=null)
        {
            leftNode[1].right = head;
            head.letf = leftNode[1];
        }
        if(rightNode[0]!=null)
        {
            rightNode[0].letf =head;
            head.right = rightNode[0];
        }
        //整个链表的头结点就是左子树的头结点,如果左子树为空,则头结点为head
        Node left = leftNode[0]!=null?leftNode[0]:head;
        //尾结点就是右子树的尾结点,如果右子树尾结点为空,则头结点为head
        Node right = rightNode[1]!=null?rightNode[1]:head;
        return new Node[] {left,right};
    }

 

【题目五】

给定数组arr和整数num,共返回有多少个子数组满足如下情况:
max(arr[i..j]) - min(arr[i..j]) <= num
max(arr[i..j])表示子数组arr[i..j]中的最大值,min(arr[i..j])表示子数组arr[i..j]中的最小值。
【要求】
如果数组长度为N,请实现时间复杂度为O(N)的解法。

【思路】

使用两个窗口更新结构记录窗口内的最大值max与最小值min

从0开始往右扩,每扩一步都检查是否达标(max-min<=num),如达标则继续扩,一直扩到不达标或越界。

当达标时位置为R,则有R+1个从0开始的子数组达标。

下一步出0位置,从1位置为头,R位置起始继续尝试往右扩,一直扩到不达标或越界。此时有R2-1+1个子数组达标,累加子数组

【Code】

public static int getNum(int[] arr,int num){
        if(arr==null || arr.length==0)
            return 0;
        //窗口用双头队列实现
        LinkedList<Integer>qmin = new LinkedList<Integer>();
        LinkedList<Integer>qmax = new LinkedList<Integer>();
        int i=0;
        int j=0;
        int res = 0;//结果
        while(i<arr.length)
        {
            while(j<arr.length)
            {
                //更新两窗口内的最大值与最小值内容
                while(!qmin.isEmpty() && arr[qmin.peekLast()] >=arr[j] )
                    qmin.pollLast();
                qmin.addLast(j);
                while(!qmax.isEmpty() && arr[qmax.peekLast()] <=arr[j]  )
                    qmax.pollLast();
                qmax.addLast(j);
                //判断是否达标
                if(arr[qmax.getFirst()] - arr[qmin.getFirst()] > num )
                    break;
                j++;
            }
            //窗口内最大最小值是否过期
            if(qmin.peekFirst()==i)
                qmin.pollFirst();
            if(qmax.peekFirst()==i)
                qmax.pollFirst();
            //累加结果
            res+=j-i;
            i++;
        }
        return res;
    }
posted @ 2021-02-24 14:41  γGama  阅读(48)  评论(0编辑  收藏  举报