剑指offer_6

剑指offer_6(每日5题)来源:牛客网

1. 连续子数组的最大和

题目:

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

分析:

使用动态规划

F(i):以array[i]为末尾元素的子数组的和的最大值,子数组的元素的相对位置不变

F(i)=max(F(i-1)+array[i] , array[i])

res:所有子数组的和的最大值

res=max(res,F(i))

如数组[6, -3, -2, 7, -15, 1, 2, 2]

初始状态:

​ F(0)=6

​ res=6

i=1:

​ F(1)=max(F(0)-3,-3)=max(6-3,3)=3

​ res=max(F(1),res)=max(3,6)=6

i=2:

​ F(2)=max(F(1)-2,-2)=max(3-2,-2)=1

​ res=max(F(2),res)=max(1,6)=6

i=3:

​ F(3)=max(F(2)+7,7)=max(1+7,7)=8

​ res=max(F(2),res)=max(8,6)=8

i=4:

​ F(4)=max(F(3)-15,-15)=max(8-15,-15)=-7

​ res=max(F(4),res)=max(-7,8)=8

以此类推

最终res的值为8

参考代码:

/*大神:动态规划*/
public  int FindGreatestSumOfSubArray(int[] array) {
        int res = array[0]; //记录当前所有子数组的和的最大值
        int max = array[0];   //包含array[i]的连续数组最大值
        for (int i = 1; i < array.length; i++) {
            max=Math.max(max+array[i], array[i]);
            res=Math.max(max, res);
        }
        return res;
}

/*大神:动态规划*/
int FindGreatestSumOfSubArray(vector<int> array) {
        if(array.empty()) return 0;
        int sum = array[0], tempsum = array[0]; //注意初始值 不能设为0 防止只有负数
        for(int i = 1; i < array.size(); i++) //从1开始 因为0的情况在初始化时完成了
        {
            tempsum = (tempsum < 0) ? array[i] : tempsum + array[i];
            sum = (tempsum > sum) ? tempsum : sum;
        }
        return sum;
    }


/*常规*/
public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        if (array.length == 0) {
            return 0;
        }
        int max,sum;
        max=array[0];
        for(int i=0; i<array.length; ++i){
            sum=0;
            for(int j=i; j<array.length; ++j){
                sum += array[j];
                if(max<sum){
                    max=sum;
                }
            }
        }
        return max;
    }
}

2. 最小的K个数

题目:

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

分析:

使用各种排序方法由小到大排序,输出前k个数即可

参考代码:

#以python为例
方法一:蒂姆排序
# -*- coding:utf-8 -*-
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        if tinput == [] or k > len(tinput):
            return []
        tinput.sort()
        return tinput[: k]
    
方法二:快速排序
# -*- coding:utf-8 -*-
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        def quick_sort(lst):
            if not lst:
                return []
            pivot = lst[0]
            left = quick_sort([x for x in lst[1: ] if x < pivot])
            right = quick_sort([x for x in lst[1: ] if x >= pivot])
            return left + [pivot] + right
 
        if tinput == [] or k > len(tinput):
            return []
        tinput = quick_sort(tinput)
        return tinput[: k]
    
方法三:归并排序
# -*- coding:utf-8 -*-
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        def merge_sort(lst):
            if len(lst) <= 1:
                return lst
            mid = len(lst) // 2
            left = merge_sort(lst[: mid])
            right = merge_sort(lst[mid:])
            return merge(left, right)
        def merge(left, right):
            l, r, res = 0, 0, []
            while l < len(left) and r < len(right):
                if left[l] <= right[r]:
                    res.append(left[l])
                    l += 1
                else:
                    res.append(right[r])
                    r += 1
            res += left[l:]
            res += right[r:]
            return res
        if tinput == [] or k > len(tinput):
            return []
        tinput = merge_sort(tinput)
        return tinput[: k]
    
方法四:堆排序
# -*- coding:utf-8 -*-
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        def siftup(lst, temp, begin, end):
            if lst == []:
                return []
            i, j = begin, begin * 2 + 1
            while j < end:
                if j + 1 < end and lst[j + 1] > lst[j]:
                    j += 1
                elif temp > lst[j]:
                    break
                else:
                    lst[i] = lst[j]
                    i, j = j, 2 * j + 1
            lst[i] = temp
 
        def heap_sort(lst):
            if lst == []:
                return []
            end = len(lst)
            for i in range((end // 2) - 1, -1, -1):
                siftup(lst, lst[i], i, end)
            for i in range(end - 1, 0, -1):
                temp = lst[i]
                lst[i] = lst[0]
                siftup(lst, temp, 0, i)
            return lst
 
        if tinput == [] or k > len(tinput):
            return []
        tinput = heap_sort(tinput)
        return tinput[: k]
    
方法五:冒泡排序
# -*- coding:utf-8 -*-
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        def bubble_sort(lst):
            if lst == []:
                return []
            for i in range(len(lst)):
                for j in range(1, len(lst) - i):
                    if lst[j-1] > lst[j]:
                        lst[j-1], lst[j] = lst[j], lst[j-1]
            return lst
 
        if tinput == [] or k > len(tinput):
            return []
        tinput = bubble_sort(tinput)
        return tinput[: k]
    
方法六:直接选择排序
# -*- coding:utf-8 -*-
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        def select_sort(lst):
            if lst == []:
                return []
            for i in range(len(lst)-1):
                smallest = i
                for j in range(i, len(lst)):
                    if lst[j] < lst[smallest]:
                        smallest = j
                lst[i], lst[smallest] = lst[smallest], lst[i]
 
            return lst
 
        if tinput == [] or k > len(tinput):
            return []
        tinput = select_sort(tinput)
        return tinput[: k]
    
方法七:插入排序
# -*- coding:utf-8 -*-
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        def Insert_sort(lst):
            if lst == []:
                return []
            for i in range(1, len(lst)):
                temp = lst[i]
                j = i
                while j > 0 and temp < lst[j - 1]:
                    lst[j] = lst[j - 1]
                    j -= 1
                lst[j] = temp
            return lst
 
        if tinput == [] or k > len(tinput):
            return []
        tinput = Insert_sort(tinput)
        return tinput[: k]

3. 数组中出现次数超过一半的数字

题目:

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

分析:

采用阵地攻守的思想:
第一个数字作为第一个士兵,守阵地;count = 1;
遇到相同元素,count++;
遇到不相同元素,即为敌人,同归于尽,count–;当遇到count为0的情况,又以新的i值作为守阵地的士兵,继续下去,到最后还留在阵地上的士兵,有可能是主元素。
再加一次循环,记录这个士兵的个数看是否大于数组一般即可。

参考代码:

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        int length=array.length;
        if(array==null||length<=0){
            return 0;
        }
        
        int result=array[0];
        int times=1;
        for(int i=1;i<length;i++){
            if(times==0){
                result=array[i];
                times=1;
            }else{
                if(array[i]==result){
                    times++;
                }else{
                    times--;
                }
            }
        }
        
        times=0;
        for(int i=0;i<length;i++){
            if(result==array[i]){
                times++;
            }
        }
            
        if(times*2<=length){
            result=0;
        }
        return result;
    }
}

4. 字符串的排列

题目:

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。( 输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。)

分析:

 //递归的思想与栈的入栈和出栈是一样的,某一个状态遇到return结束了之后,会回到被调用的地方继续执行
 //1.第一次进到这里是ch=['a','b','c'],list=[],i=0,我称为 状态A ,即初始状态
 //那么j=0,swap(ch,0,0),就是['a','b','c'],进入递归,自己调自己,只是i为1,交换(0,0)位置之后的状态我称为 状态B 
 //i不等于2,来到这里,j=1,执行第一个swap(ch,1,1),这个状态我称为 状态C1 ,再进入fun函数,此时标记为T1,i为2,
 那么这时就进入上一个if,将"abc"放进list中
 /-------》此时结果集为["abc"]
        
 //2.执行完list.add之后,遇到return,回退到T1处,接下来执行第二个swap(ch,1,1),状态C1又恢复为状态B
 //恢复完之后,继续执行for循环,此时j=2,那么swap(ch,1,2),得到"acb",这个状态我称为C2,然后执行fun,此时标记为T2,
 发现i+1=2,所以也被添加进结果集,此时return回退到T2处往下执行
/-------》此时结果集为["abc","acb"]
//然后执行第二个swap(ch,1,2),状态C2回归状态B,然后状态B的for循环退出回到状态A
        
 //             a|b|c(状态A)
 //               |
 //               |swap(0,0)
 //               |
 //             a|b|c(状态B)
 //             /  \
 //   swap(1,1)/    \swap(1,2)  (状态C1和状态C2)
 //           /      \
 //         a|b|c   a|c|b
        
 //3.回到状态A之后,继续for循环,j=1,即swap(ch,0,1),即"bac",这个状态可以再次叫做状态A,下面的步骤同上
  /-------》此时结果集为["abc","acb","bac","bca"]
        
  //             a|b|c(状态A)
  //               |
  //               |swap(0,1)
  //               |
  //             b|a|c(状态B)
  //             /  \
  //   swap(1,1)/    \swap(1,2)  (状态C1和状态C2)
  //           /      \
  //         b|a|c   b|c|a
        
  //4.再继续for循环,j=2,即swap(ch,0,2),即"cab",这个状态可以再次叫做状态A,下面的步骤同上
  /-------》此时结果集为["abc","acb","bac","bca","cab","cba"]
        
  //             a|b|c(状态A)
  //               |
  //               |swap(0,2)
  //               |
  //             c|b|a(状态B)
  //             /  \
  //   swap(1,1)/    \swap(1,2)  (状态C1和状态C2)
  //           /      \
  //         c|b|a   c|a|b
      
 //5.最后退出for循环,结束。

参考代码:

import java.util.List;
import java.util.Collections;
import java.util.ArrayList;
 
public class Solution { 
    public ArrayList<String> Permutation(String str) {
        List<String> res = new ArrayList<>();
        if (str != null && str.length() > 0) {
            //递归的初始值为(str数组,空的list,初始下标0)
            PermutationHelper(str.toCharArray(), 0, res);
            Collections.sort(res);
        }
        return (ArrayList)res;
    }
 
    public void PermutationHelper(char[] cs, int i, List<String> list) {
        //这是递归的终止条件,就是i下标已经移到char数组的末尾的时候,考虑添加这一组字符串进入结果集中
        if (i == cs.length - 1) {
            String val = String.valueOf(cs);
            if (!list.contains(val))                 //判断一下是否重复
                list.add(val);
        } else {
            for (int j = i; j < cs.length; j++) {    //这一段就是回溯法,这里以"abc"为例
                swap(cs, i, j);
                PermutationHelper(cs, i+1, list);
                swap(cs, i, j);
            }
        }
    }
 
    public void swap(char[] cs, int i, int j) {
        char temp = cs[i];
        cs[i] = cs[j];
        cs[j] = temp;
    }
}

5. 二叉搜索树与双向链表

题目:

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

参考代码:

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
 
    public TreeNode(int val) {
        this.val = val;
 
    }
 
}
*/
 
//直接用中序遍历
public class Solution {
    TreeNode head = null;
    TreeNode realHead = null;
    public TreeNode Convert(TreeNode pRootOfTree) {
        ConvertSub(pRootOfTree);
        return realHead;
    }
     
    private void ConvertSub(TreeNode pRootOfTree) {
        if(pRootOfTree==null) return;
        ConvertSub(pRootOfTree.left);
        if (head == null) {
            head = pRootOfTree;
            realHead = pRootOfTree;
        } else {
            head.right = pRootOfTree;
            pRootOfTree.left = head;
            head = pRootOfTree;
        }
        ConvertSub(pRootOfTree.right);
    }
}



/*其它思路*/
方法一:非递归版
解题思路:
1.核心是中序遍历的非递归算法。
2.修改当前遍历节点与前一遍历节点的指针指向。
    import java.util.Stack;
    public TreeNode ConvertBSTToBiList(TreeNode root) {
        if(root==null)
            return null;
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode p = root;
        TreeNode pre = null;// 用于保存中序遍历序列的上一节点
        boolean isFirst = true;
        while(p!=null||!stack.isEmpty()){
            while(p!=null){
                stack.push(p);
                p = p.left;
            }
            p = stack.pop();
            if(isFirst){
                root = p;// 将中序遍历序列中的第一个节点记为root
                pre = root;
                isFirst = false;
            }else{
                pre.right = p;
                p.left = pre;
                pre = p;
            }      
            p = p.right;
        }
        return root;
    }
方法二:递归版
解题思路:
1.将左子树构造成双链表,并返回链表头节点。
2.定位至左子树双链表最后一个节点。
3.如果左子树链表不为空的话,将当前root追加到左子树链表。
4.将右子树构造成双链表,并返回链表头节点。
5.如果右子树链表不为空的话,将该链表追加到root节点之后。
6.根据左子树链表是否为空确定返回的节点。
    public TreeNode Convert(TreeNode root) {
        if(root==null)
            return null;
        if(root.left==null&&root.right==null)
            return root;
        // 1.将左子树构造成双链表,并返回链表头节点
        TreeNode left = Convert(root.left);
        TreeNode p = left;
        // 2.定位至左子树双链表最后一个节点
        while(p!=null&&p.right!=null){
            p = p.right;
        }
        // 3.如果左子树链表不为空的话,将当前root追加到左子树链表
        if(left!=null){
            p.right = root;
            root.left = p;
        }
        // 4.将右子树构造成双链表,并返回链表头节点
        TreeNode right = Convert(root.right);
        // 5.如果右子树链表不为空的话,将该链表追加到root节点之后
        if(right!=null){
            right.left = root;
            root.right = right;
        }
        return left!=null?left:root;       
    }
方法三:改进递归版
解题思路:
思路与方法二中的递归版一致,仅对第2点中的定位作了修改,新增一个全局变量记录左子树的最后一个节点。
    // 记录子树链表的最后一个节点,终结点只可能为只含左子树的非叶节点与叶节点
    protected TreeNode leftLast = null;
    public TreeNode Convert(TreeNode root) {
        if(root==null)
            return null;
        if(root.left==null&&root.right==null){
            leftLast = root;// 最后的一个节点可能为最右侧的叶节点
            return root;
        }
        // 1.将左子树构造成双链表,并返回链表头节点
        TreeNode left = Convert(root.left);
        // 3.如果左子树链表不为空的话,将当前root追加到左子树链表
        if(left!=null){
            leftLast.right = root;
            root.left = leftLast;
        }
        leftLast = root;// 当根节点只含左子树时,则该根节点为最后一个节点
        // 4.将右子树构造成双链表,并返回链表头节点
        TreeNode right = Convert(root.right);
        // 5.如果右子树链表不为空的话,将该链表追加到root节点之后
        if(right!=null){
            right.left = root;
            root.right = right;
        }
        return left!=null?left:root;       
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值