Java Leetcode中KSum算法问题详解

# Java Leetcode中KSum算法问题详解

今天写了下Leetcode里面Ksum算法题,一个算法用不同的方法实现,确实非常累,特别是写到最后一种Hash查找实现的方法,看代码逻辑都是debug一点一点的看,这里奉劝各位,写不出来,出去走走,回来在写,也许灵感就来了呢?废话不多说,看正文。
关于Leetcode K sum问题的解法目前有三种,刚开始做题我使用的是暴力解法,提交到Leetcode的时候由于时间复杂度太高,提交不过,查看关于KSum问题的更多解法的时候,发现大家都在说,化解为2sum问题,和使用hash算法实现,于是马上着手实现,从时间复杂度上看,肯定是前两种时间复杂度更低,最后也能submit过。
这里发一下题目的链接:
https://leetcode.com/problems/3sum/
三种实现方法
- 暴力法
- 最终化解为2sum问题
- hash实现法


暴力法

这种方法就是采用K个for循环实现,这里就不多说了,直接上代码,相信接触过java的人都看得懂。这里暴力法是针对Leetcode的3Sum来实现的。

import java.util.ArrayList;
import java.util.List;

/**
 * 暴力查找
 * 
 */
public class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> resultList = new ArrayList<List<Integer>>();
        List<Integer> tempList = null;
        int arrayLength = nums.length;
        if (arrayLength < 3) {
            return resultList;
        }
        System.out.print("[");
        nums = sortArray(nums);
        for (int i = 0; i < nums.length; ++i) {
            System.out.print(nums[i] + "   ");
        }
        System.out.print("]");
        System.out.println();
        int firstNum = 0, secondNum = 0, thirdNum = 0;
        for (int k = 0; k < arrayLength - 2; ++k) {
            firstNum = nums[k];
            for (int j = k + 1; j < arrayLength - 1; ++j) {
                secondNum = nums[j];
                for (int i = j + 1; i < arrayLength; ++i) {
                    thirdNum = nums[i];
                    if ((firstNum + secondNum + thirdNum) == 0) {
                        tempList = new ArrayList<Integer>();
                        tempList.add(firstNum);
                        tempList.add(secondNum);
                        tempList.add(thirdNum);
                        if (!isContains(resultList, tempList)) {
                            resultList.add(tempList);
                        }

                    }
                }
            }
        }

        return resultList;
    }

    /**
     * 排序,采用直接选择排序,从小到大排序
     * 
     * @param arrays
     * @return
     */
    private int[] sortArray(int[] arrays) {
        if (arrays.length == 0 || arrays.length == 1) {
            return arrays;
        }
        int length = arrays.length, temptemp;

        for (int i = 0; i < length - 1; ++i) {
            for (int k = i + 1; k < length; ++k) {
                if (arrays[i] > arrays[k]) {
                    temptemp = arrays[k];
                    arrays[k] = arrays[i];
                    arrays[i] = temptemp;
                }
            }

        }
        return arrays;
    }

    /**
     * 判断是否包涵
     * 
     * @param list
     * @param testNums
     * @return
     */
    private boolean isContains(List<List<Integer>> list, List<Integer> testNums) {
        boolean result = false;
        boolean temp = false;
        List<Integer> tempNums;
        for (int i = 0; i < list.size(); ++i) {
            tempNums = list.get(i);
            if (tempNums.size() != testNums.size()) {
                continue;
            }
            for (int k = 0; k < tempNums.size(); ++k) {
                if (tempNums.get(k) != testNums.get(k)) {
                    temp = true;
                    break;
                }
            }
            if (!temp) {
                result = true;
                return result;
            }
            temp = false;
        }
        return result;
    }
}

递归最后化为2sum实现

这种方法其实就是在递归到2Sum问题实现,算法思想上没有什么好解释的,看代码吧,这里我在代码里面加了大量的注释,就不在这里啰嗦了。


/**
 * 递归链式查找,Ksun问题
 * 
 */
public class HalfSolution {
    //默认是2sum问题
    private int kValue = 2;
    // 注入k值,动态实现K值问题
    public void setK(int k) {
        kValue = k < 2 ? 2 : k;
    }
    /**
     * 外部调用方法,公有权限
     * @param nums
     * @param target
     * @return
     */
    public List<List<Integer>> threeSum(int[] nums,int target) {
        return kSum(nums, kValue, target);
    }
    /**
     * 做一些判断、排序之类的的事情
     * @param nums
     * @param k
     * @param target
     * @return
     */
    private List<List<Integer>> kSum(int[] nums, int k, int target) {
        //返回结果数据集合,这里这样写是因为Leetcode接口要求
        List<List<Integer>> resultList = new ArrayList<List<Integer>>();
        // 结果集里面的每个结果元素
        ArrayList<Integer> tempList = null;
        // 判断
        if (nums.length < k) {
            return resultList;
        } else if (nums.length == k) {
            int sumValue = 0;
            tempList = new ArrayList<Integer>();
            for (int i = 0; i < nums.length; ++i) {
                sumValue += nums[i];
                tempList.add(nums[i]);
            }
            if (sumValue == target) {
                resultList.add(tempList);
            }
            return resultList;

        }
        // 数组排序
        Arrays.sort(nums);
        recusionSolution(nums, k, target, 0, tempList, resultList);
        return resultList;
    }
    /**
     * 主方法
     * @param nums 数组
     * @param k    几个值的和
     * @param sum  目标值
     * @param index 索引开始值
     * @param tempList 盛放元素结果集
     * @param resultList  最终结果集合
     */
    private void recusionSolution(int[] nums, int k, int sum, int index,
            ArrayList<Integer> tempList, List<List<Integer>> resultList) {
        // 判断是不是2sum了,做一些指针的相应移动
        if (k == 2) {
            int first = index;
            int tail = nums.length - 1;
            int first_last_value = 0;
            int tail_last_value = 0;
            boolean isMatch = false;
            while (first < tail) {
                if (nums[first] + nums[tail] == sum) {

                    if (isMatch && first_last_value == nums[first]) {
                        first++;
                        continue;
                    } else if (isMatch && tail_last_value == nums[tail]) {
                        tail--;
                        continue;
                    } else if (isMatch && tail_last_value == nums[tail]
                            && first_last_value == nums[first]) {
                        first++;
                        tail--;
                        continue;
                    }

                    ArrayList<Integer> tempList_copy = (ArrayList<Integer>) tempList
                            .clone();
                    tempList.add(nums[first]);
                    tempList.add(nums[tail]);
                    resultList.add(tempList);
                    tempList = tempList_copy;
                    isMatch = true;
                    first_last_value = nums[first];
                    tail_last_value = nums[tail];
                    first++;
                    tail--;

                } else if (nums[first] + nums[tail] > sum) {
                    tail--;
                } else if (nums[first] + nums[tail] < sum) {
                    first++;
                }
            }
        } else {
            //递归
            for (int i = index; i < nums.length - k; ++i) {
                if (k == kValue) {
                    tempList = new ArrayList<Integer>();
                }
                if (i > 0 && nums[i - 1] == nums[i]) {
                    continue;
                }
                tempList.add(nums[i]);
                recusionSolution(nums, k - 1, sum - nums[i], i + 1, tempList,
                        resultList);

            }
        }
        return;
    }
}

hash实现法

hash实现法,其实和化解为2sum问题类似,只是在最后查找一个数字的时候要用到hash查找,一般就是采用hashMap,HashTable实现,这里不推荐Hashtable,HashTable内部采用了同步关键字,效率是没有hashmap高的。
算法实现:采用hash容器承载数据,再采用递归达到K==1的位置。

/**
 * 哈希算法实现,类似于两个目标数查找
 * 
 * @author cWX404529
 * 
 */
public class HashSolution {
    // 默认是两个值
    private int kValue = 2;
    // 注入K值
    public void setK(int k) {
        kValue = k < 2 ? 2 : k;
    }

    public List<List<Integer>> threeSum(int[] nums,int target) {
        return kSum(nums, kValue,target);
    }

    private List<List<Integer>> kSum(int[] nums, int k, int target) {
        // 结果数据集合
        List<List<Integer>> resultList = new ArrayList<List<Integer>>();
        // 临时结果数据集合
        ArrayList<Integer> tempList = new ArrayList<Integer>();
        ;
        if (k <= 0 || nums.length < k) {
            return resultList;
        } else if (nums.length == k) {
            int sumValue = 0;
            for (int i = 0; i < nums.length; ++i) {
                sumValue += nums[i];
                tempList.add(nums[i]);
            }
            if (sumValue == target) {
                resultList.add(tempList);
            }
            return resultList;

        }
        // 数组排序
        Arrays.sort(nums);
        // HashMap承载数据
        HashMap<Integer, Integer> hashMap = new HashMap<Integer, Integer>();
        for (int i = 0; i < nums.length; ++i) {
            // 存储数据到hash容器
            hashMap.put(i, nums[i]);
        }
        recusionSolution(nums, k, target, 0, tempList, resultList, hashMap);

        return resultList;
    }
    /**
     * tempList_1相当于数据栈,有数据的入栈和出栈,add()和remove()都是配对出现
     * @param nums 源数组
     * @param k   K值
     * @param sum   目标值
     * @param index  开始索引值
     * @param tempList 承载临时数据容器
     * @param resultList 返回结果
     * @param hashMap1 数组的hash容器实现
     */
    private void recusionSolution(int[] nums, int k, int sum, int index,
            ArrayList<Integer> tempList, List<List<Integer>> resultList,
            HashMap<Integer, Integer> hashMap1) {
        int last_value = nums[index] - 1;
        // 剔除的数据再返回上一次递归的时候,要还原数据,这里装载剔除的数据
        HashMap<Integer, Integer> temp_HashMap__1 = new HashMap<Integer, Integer>();
        // K值等于1时采用hash判断是否有值
        if (k == 1) {
            if (hashMap1.containsValue(sum)) { // 调用Hash容器方法
                tempList.add(sum);
                 // 谢天谢地这里是深复制,不然要自己实现。复制一份数据放入结果集中
                ArrayList<Integer> tempList_1 = (ArrayList<Integer>) tempList
                        .clone();
                resultList.add(tempList_1);
                tempList.remove(kValue - k);
            }
        } else if (k > 1) {
            for (int i = index; i < nums.length; ++i) {
                if (k == kValue) {
                    tempList = new ArrayList<Integer>();
                }
                hashMap1.remove(i);
                // 调用Hash容器方法
                temp_HashMap__1.put(i, nums[i]);
                // 这里判断前面一个数据是否相同,如果相同就不用再进入判读,直接跳过
                if (last_value == nums[i]) {
                    last_value = nums[i];
                    continue;
                }
                last_value = nums[i];
                tempList.add(nums[i]);
                if (hashMap1.size() > 0) {
                    recusionSolution(nums, k - 1, sum - nums[i], i + 1,
                            tempList, resultList, hashMap1);
                }
                tempList.remove(kValue - k);
                last_value = nums[i];
            }
        }
        //  归还临时剔除的数据,为下次递归做准备
        hashMap1.putAll(temp_HashMap__1);
        return;
    }

}

总结

    整个Ksum算法实现并不复杂,实现起来只要逻辑清楚,能很快的写出来,这里要提一下的是,HashMap的Clone()方法是深复制(谢天谢地,不然要自己实现了),包括ArrayList也是,感兴趣的可以自己写demo测试一下。网上也看别人用的Hashable实现,但是我看他貌似没有考虑到数据重复的问题,这一点大家要知道,源数据是可以重复的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值