ARTS-9(回溯法的组合总和系列,泛型总结、人生的意义在于体验)

Algorithm

题目描述

leecode 39. 组合总和
给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。
candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。
对于给定的输入,保证和为 target 的唯一组合数少于 150 个。
示例:

输入: candidates = [2,3,6,7], target = 7
输出: [[7],[2,2,3]]
输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
输入: candidates = [2], target = 1
输出: []

题目详解
参考:代码随想录
关于回溯法
在这里插入图片描述

大部分情况下,回溯法都是用来解决广义的搜索问题,也就是,从一组可能的解中,选择出一个满足要求的解。回溯算法非常适合用递归来实现,在实现的过程中,剪枝操作是提高回溯效率的一种技巧。利用剪枝,我们并不需要穷举搜索所有的情况,从而提高搜索效率。

代码实现

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        backtracking(candidates,target,new ArrayList<>(),0,res);
        return res;
    }
    public void backtracking(int[] candidates,int target,List<Integer> list,int index,List<List<Integer>> res){
        if(target<0)
            return;
        if(target==0){
        	// // 找到了数字和为 target 的组合
            res.add(new ArrayList<>(list));
            return;
        }else{
            for(int i=index;i<candidates.length;i++){
                list.add(candidates[i]);
                dfs(candidates,target-candidates[i],list,i,res);
                list.remove(list.size()-1); 回溯,移除路径 path 最后一个元素
            }
        }
    }
}

Review

泛型的深入理解

1、什么是泛型
自JDK 1.5 之后,Java 通过泛型解决了容器类型安全这一问题,而几乎所有人接触泛型也是通过Java的容器。那么泛型究竟是什么?
泛型的本质是参数化类型
也就是说,泛型就是将所操作的数据类型作为参数的一种语法。

public class Paly<T>{
    T play(){}
}

其中T就是作为一个类型参数在Play被实例化的时候所传递来的参数,比如:
Play<Integer> playInteger=new Play<>();
这里T就会被实例化为Integer.
2、引入泛型的优点

  • 类型安全,可以在编译期就检查出因 Java 类型不正确导致的 ClassCastException 异常
  • 消除强制类型转换,即所得即所需,使用的时候直接得到目标类型
  • 潜在的性能收益,参数化类型这个过程是在编译器里完成的,编译器生成的代码,使用泛型和不使用泛型,是一样的。

3、泛型的使用方式
这种参数化类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

public class GenericClass<F> {
    private F mContent;

    public GenericClass(F content){
        mContent = content;
    }

    /**
     * 泛型方法
     * @return
     */
    public F getContent() {
        return mContent;
    }

    public void setContent(F content) {
        mContent = content;
    }

    /**
     * 泛型接口
     * @param <T>
     */
    public interface GenericInterface<T>{
        void doSomething(T t);
    }
}

泛型类
类名中声明参数类型后,内部成员、方法就可以使用这个参数类型,比如上面的 GenericClass 就是一个泛型类,它在类名后声明了类型 F,它的成员、方法就可以使用 F 表示成员类型、方法参数/返回值都是 F 类型。常见的泛型类就是java的集合类。
泛型接口
和泛型类一样,泛型接口在接口名后添加类型参数,比如上面的 GenericInterface,接口声明类型后,接口方法就可以直接使用这个类型。实现类在实现泛型接口时需要指明具体的参数类型,不然默认类型是 Object。
泛型方法
泛型方法是指使用泛型的方法,如果它所在的类是个泛型类,那就很简单了,直接使用类声明的参数。
如果一个方法所在的类不是泛型类,或者他想要处理不同于泛型类声明类型的数据,那它就需要自己声明类型,举个例子:

/**
 * 泛型方法,介于方法修饰符和返回值之间的称作 类型参数列表 <A,V,F,E...> (可以有多个)
 *      类型参数列表 指定参数、返回值中泛型参数的类型范围,命名惯例与泛型相同
 * @param s1
 * @param s2
 * @param <E>
 * @return
 */
public <E> Set<E> union2(Set<E> s1, Set<E> s2){
    Set<E> result = new HashSet<>(s1);
    result.addAll(s2);
    return result;
}

Tips

总结一个elasticsearch加载数据的时候的连接超时报错
报错内容

java.io.IOException: listener timeout after waiting for [30000] ms
	at org.elasticsearch.client.RestClient$SyncResponseListener.get(RestClient.java:899)
	at org.elasticsearch.client.RestClient.performRequest(RestClient.java:227)
	at org.elasticsearch.client.RestHighLevelClient.performRequest(RestHighLevelClient.java:1256)
	at org.elasticsearch.client.RestHighLevelClient.performRequestAndParseEntity(RestHighLevelClient.java:1231)
	at org.elasticsearch.client.RestHighLevelClient.bulk(RestHighLevelClient.java:329)
	at com.changjia.commodity.task.util.EsUtil.saveAll(EsUtil.java:146)

问题根因
es写入数据的时候,当数据量过大,加载时间超出es默认连接时长时候,就会出现该错误。
解决方案
在获取es连接的时候,将过期时长设置的更大

public class EsHighLevelClient {
    private static Logger logger = LoggerFactory.getLogger(EsHighLevelClient.class);

    public static RestHighLevelClient getmHighLevelClient(String ips,String userName,String password){
        String[] hosts = null;
        if(ips.contains(",")){
            hosts = ips.split(",");
        }else{
            hosts = new String[]{ips};
        }
        HttpHost[] httpHosts = new HttpHost[hosts.length];
        for(int i=0;i<hosts.length;i++) {
            httpHosts[i] = new HttpHost(hosts[i].split(":")[0], 9200, "http");
        }
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(httpHosts)
                        //.setDefaultHeaders(defaultHeaders)
                        .setMaxRetryTimeoutMillis(300 * 1000) // 超时时间
                        .setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {
                            @Override
                            public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) {
                                requestConfigBuilder.setConnectTimeout(30000);
                                requestConfigBuilder.setSocketTimeout(300 * 1000);
                                requestConfigBuilder.setConnectionRequestTimeout(-1);
                                return requestConfigBuilder;
                            }
                        })
                        .setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
                            @Override
                            public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpAsyncClientBuilder) {
                                httpAsyncClientBuilder.disableAuthCaching();
                                return httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                            }
                        })
                        .setFailureListener(new RestClient.FailureListener() {
                            @Override
                            public void onFailure(Node node) {
                                super.onFailure(node);
                                logger.error(node.getName() + "节点失败了");
                            }
                        }));
        return client;
    }
}

记录下通过查看源码查询问题根因所在
1、首先看异常日志中显示的是RestClient$SyncResponseListener.get(RestClient.java:899)抛出了异常,看RestClient类中相关的代码发现CountDownLatch锁存器等待了timeout毫秒还没有等待返回值,就会抛出异常listener timeout after waiting for [30000] ms,因为timeout是关键所在

Response get() throws IOException {
            try {
                if (!this.latch.await(this.timeout, TimeUnit.MILLISECONDS)) {
                    throw new IOException("listener timeout after waiting for [" + this.timeout + "] ms");
                }
            } catch (InterruptedException var4) {
                throw new RuntimeException("thread waiting for the response was interrupted", var4);
            }
    }

2、找出timeout是如何进行参数设置的,看RestClient类的静态内部类SyncResponseListener的构造方法发现设置了该过期时长

static class SyncResponseListener implements ResponseListener {
        private final CountDownLatch latch = new CountDownLatch(1);
        private final AtomicReference<Response> response = new AtomicReference();
        private final AtomicReference<Exception> exception = new AtomicReference();
        private final long timeout;

        SyncResponseListener(long timeout) {
            assert timeout > 0L;

            this.timeout = timeout;
        }

3、找出调用该构造方法的地方,发现是在RestClient中的方法设置了这个过期时长

public Response performRequest(Request request) throws IOException {
        RestClient.SyncResponseListener listener = new RestClient.SyncResponseListener(this.maxRetryTimeoutMillis);
        this.performRequestAsyncNoCatch(request, listener);
        return listener.get();
    }

4、maxRetryTimeoutMillis是RestClient的成员变量,是在调用构造方法的时候传入,其中RestClient构造方法是在RestClientBuilder的方法中构建的

public RestClient build() {
        if (this.failureListener == null) {
            this.failureListener = new FailureListener();
        }

        CloseableHttpAsyncClient httpClient = (CloseableHttpAsyncClient)AccessController.doPrivileged(new PrivilegedAction<CloseableHttpAsyncClient>() {
            public CloseableHttpAsyncClient run() {
                return RestClientBuilder.this.createHttpClient();
            }
        });
        RestClient restClient = new RestClient(httpClient, (long)this.maxRetryTimeout, this.defaultHeaders, this.nodes, this.pathPrefix, this.failureListener, this.nodeSelector, this.strictDeprecationMode);
        httpClient.start();
        return restClient;
    }

5、追溯到RestClientBuilder方法中的成员变量maxRetryTimeoutMillis默认是30s,可以通过以下方法进行设置

public RestClientBuilder setMaxRetryTimeoutMillis(int maxRetryTimeoutMillis) {
        if (maxRetryTimeoutMillis <= 0) {
            throw new IllegalArgumentException("maxRetryTimeoutMillis must be greater than 0");
        } else {
            this.maxRetryTimeout = maxRetryTimeoutMillis;
            return this;
        }
    }

总结
首先找到发生异常的出处所在,进而根据回溯来找出异常的整条链路,找出问题的解决办法

Share

今天早起跑步在得到听了万维钢老师解读的《强力瞬间》,其实无论工作还是生活还是与人交友,灵光闪现的idea、突然的恍然大悟、又或者意识的瞬间转变,都可能是这场体验中一个个精彩绝伦的瞬间,它给予你的不仅是当时的精彩感受也可能会带给你更加深远的影响。

作者就讲到,无论是你想让对方感受还是对方想让你感受,都是可以进行体验的设计。

第一方面是制造洞见时刻。这让我想起了,有些优秀的大学老师或者一些培训老师,总是在开头的时候,喜欢先从讲故事或者实际体验来开始来引入话题的中心思想,他们会想让你先产生疑问或者先让你把通过亲身经历他们设计的体验游戏,进而将你带入这场真正的话题里。在这场交涉过程中,通过他们精心设计的体验,给到你某些瞬间的体验,这是非常有意义的,无论是对教授者还是对于被教授者都是一次非常成功的过程。想要让对方真正意识到一些问题,或许可以在交涉之前精心设计一个体验,这点特别让我觉得特别适合放在小孩教育上,经常会有大人说,和小孩讲道理是讲不通的,设计一些体验让小孩亲自去感受一些事情,可能能让孩子真正意识到问题的所在。

第二方面是如何设计荣耀感。人的一生不能总是经历挫折,不然再强大的自信也会被慢慢地削减,我们需要一些有强烈荣誉感的高光时刻,需要一些具体的认可。文章中就提到,成功人士有一个共同的而特点:他们对“完成”一件事很执着,“完成”这个动作能给他们带来极大的荣誉感。可见荣誉感在某些方面是能够给人带来能量的。比如当我完成30篇ARTS的时候我就奖励自己一次外出旅游,当我拍到十套人像专辑的时候我就给自己买个镜头,当我跑到100公里的时候我就给自己买一直舍不得买的护肤品,多有意思~

“荣誉感是来自一小步一小步切实的成就,荣誉感来自别人的认可,荣誉感来自关键时刻表现出来的勇敢。”划定合适的目标,小步伐式的前进,不断的为“完成”设计阶段荣誉感,感受实实在在的进步,要相信慢慢来比较快~

第三方面是如何设计人与人之间的关系。有三个公式,第一个是:团队凝聚力=目标感+一起奋斗的经历+协调同步。很多企业或者团队能够有成功,必须是这个团队的每一个都是在朝着一个目标在不断的付出自己的一份努力,没有谁拉谁的后腿,也没有谁停滞不前,这才是真正的凝聚力吧,

第二个是:快速的亲密关系=开放,这让我想起了剧集我们这一天里,梗在凯特和托比之间最大的问题就是凯特不愿把自己内心关于她父亲去世的那些事情告知给托比,托比始终觉得没有走进凯特的生活里,当凯特决定袒露自己内心最深的那些事情之后,两个人关系也变的更加亲密。学会对自己喜欢的人坦陈布公,这才是你们亲密关系真正的开始。连岳老师也曾说过,好的感情必须真诚。

第三个是:长期的好关系=响应=理解+接收+关心。这个总结真的很棒,分享一下:所谓“响应”,由三点组成。第一是理解。你了解我,而且你还了解我自己是怎么看待我自己的,什么东西对我最重要。第二是接受。我想要什么,我是什么样的人,你得对此表示尊重。第三是关心。就是在各种场合下,一旦我需要什么帮助,你得帮助我。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值