会员可以在此提问,百战程序员老师有问必答
对大家有帮助的问答会被标记为“推荐”
看完课程过来浏览一下别人提的问题,会帮你学得更全面
截止目前,同学们一共提了 132359个问题
JAVA 全系列/第二阶段:JAVA 基础深化和提高/网络编程(旧) 361楼

老师好,关于remove有如下算法和问题:


题目描述:

https://leetcode.com/problems/combination-sum/



解答:



class Solution {

    public List<List<Integer>> combinationSum(int[] candidates, int target) {

        List<List<Integer>> res= new ArrayList<>();

        helper(res,candidates, target, new ArrayList<>(),0);

        return res;

    }

    private void helper(List<List<Integer>> res, int[] nums, int target, List<Integer> temp, int start){

        if(target==0){

            res.add(new ArrayList<>(temp));

        }else if(target <0) return;

        for(int i=start;i<nums.length; i++){

            temp.add(nums[i]);

            helper(res, nums,target-nums[i], temp, i);

            temp.remove(temp.size()-1);

        }

    }

}



我有两个问题:

1.

14行

            temp.remove(temp.size()-1);

arraylist的remove方法可以输入index的方法,或者直接输入object来找到对应元素,并删除。

这里因为temp内本身往里面add的元素,也是integer,所以我不太懂,为什么在这里remove时,java会判定是按照index去remove(即remove掉最后一个元素),而不是去找temp.size()-1 对应的value,去remove temp里的元素呢?



2.

11行,

 for(int i=start;i<nums.length; i++){


这里我一开始写成 int i =0; 

我觉得虽然方法内部写错了,但算法本身应该没错,因为在第四行调用函数时,

helper(res,candidates, target, new ArrayList<>(),0);

start的初始值就是给的0,所以我觉得应该这里写int =0 也不会有错,但是程序结果有错,


当 int=0

输入:

[2,3,6,7]
7

输出为:

[[2,2,3],[2,3,2],[3,2,2],[7]]



但是改成int i= start后,输出为

[[2,2,3],[7]]

为正确结果。


我没搞懂为什么会出现这个差异?






JAVA 全系列/第二阶段:JAVA 基础深化和提高/容器(旧) 363楼
JAVA 全系列/第二阶段:JAVA 基础深化和提高/IO流技术 367楼
JAVA 全系列/第二阶段:JAVA 基础深化和提高/多线程技术(旧) 370楼

问题描述:老师,我在手写LinkeList的时候,add(index,element)无法插入,代码检查也没有什么问题,能帮忙分析一下么?代码如下

package com.test.Homework;

import com.test.Node;

import java.util.LinkedList;

public class MyLinkedList<E> {

    private MyNode first;
    private MyNode last;
    private int size;

    public static void main(String[] args) {
        MyLinkedList<String> ss = new MyLinkedList<String>();
        ss.add("a");
        ss.add("b");
        ss.add("c");
        ss.add("d");
        ss.add("e");
        ss.add("f");
        ss.add("g");
        System.out.println(ss);
        ss.remove(0);
        System.out.println(ss.get(0));
        ss.add(2,"jk");
        System.out.println(ss);
    }
    @Override
    public String toString() {
        StringBuilder str = new StringBuilder("[");
        MyNode temp = first;
        while (temp!=null){
            str.append(temp.element+",");
            temp = temp.next;
        }
        str.setCharAt(str.length()-1,']');
        return str.toString();
    }
    private void checkRange(int index){//为了保证程序健壮性
        if (index<0||index>size-1){
            throw new RuntimeException("索引不合法:" + index);
        }
    }
    public MyNode getNode(int index){//通过索引获得数据信息
        MyNode temp = null;
        if(index<=(size>>1)){
            temp=first;
            for(int i=0;i<index;i++){
                temp = temp.next;
            }
        }else{
            temp = last;
            for(int i=size-1;i>index;i--){
                temp = temp.previous;
            }
        }
        return temp;
    }
    public E get(int index){//通过index获取数据域,与getNode 功能一样
        checkRange(index);
        MyNode temp = getNode(index);
        System.out.println(temp);
        return temp!=null? (E)temp.element:null;
    }

    public void add(E element){//仅添加数据
        MyNode node = new MyNode(element);
        if(first==null){//第一次放数据
            node.previous = first;
            node.next = null;
            first=node;
            last=node;
        }else{
            node.previous=first;
            node.next=null;
            last.next=node;
            last=node;
        }
    }
    /**public void add(int index, E element) {
        if(size<=index){
        add(element);
        return;
        }
        MyNode pNode = getNode(index);// 插入节点
        MyNode preNode = pNode.previous; // 插入节点的前驱节点
        MyNode newNode = new MyNode();// 新节点
        newNode.previous=preNode;// 首先, 新节点的前驱指向插入节点的前驱节点
        newNode.next = pNode;// 其次, 新节点的后继指向插入节点
        pNode.previous = newNode;// 然后, 插入节点的前驱指向新节点
        preNode.next = newNode;// 最后, 插入节点的前驱结点的后继指向新节点
        newNode.element = element;// 节点的数据域赋值
        if (index == 0)
        first = newNode;
            if(size ==index)
            last = newNode;
            size++;
        }*/

   public void add(int index,E element){
       checkRange(index);
       MyNode newNode = new MyNode(element);
       MyNode temp = getNode(index);
       if(temp!=null){
           MyNode up = temp.previous;
           up.next = newNode;
           newNode.previous = up;
           newNode.next = temp;
           temp.previous = newNode;
       }
   }
   public void remove(int index){//删除方法
        checkRange(index);
        MyNode temp = getNode(index);
        if(temp!=null){
            MyNode up = temp.previous;
            MyNode down = temp.next;
            if(up!=null){
                up.next=down;
            }
            if(down!=null){
                down.previous=up;
            }
            if(index==0){
                first=down;
            }
            if(index==size-1){
                last=up;
            }
            size--;
        }
    }
}


class MyNode{//创建一个节点类
    MyNode previous;//前驱
    MyNode next;//后续
    Object element;//数据域

    public MyNode() {
        this.previous = previous;
        this.next = next;
        this.element = element;
    }
    public MyNode(Object element){
        this.element=element;
    }
}


JAVA 全系列/第二阶段:JAVA 基础深化和提高/容器(旧) 371楼

照著老師的代码敲的找不到为什么会报这个错误


image.png

package com.bjsxt;

public class MySinglyLinkedList<E> implements MyList<E> {
    class Node<E>{
        private E item;
        private Node next;
        Node(E item, Node next){
            this.item =item;
            this.next = next;
        }
    }
    private Node head;
    private int size;
    @Override
    public void add(E element) {
        Node<E> node = new Node<>(element,null);
        Node tail = getTail();
        if (tail == null){
            this.head = node;
        }else{
            tail.next = node;
        }
        this.size++;


    }
    private Node getTail(){
        if(this.head == null){
            return null;
        }
        Node node = this.head;
        while (true){
            if(node.next == null)break;
            node = node.next;

        }
        return node;
    }

    @Override
    public E get(int index) {
        this.checkIndex(index);
        Node<E> node = this.getNode(index);
        return node.item;
    }

    private void checkIndex(int index){
        if(!(index >= 0 && index < this.size)){
            throw new IndexOutOfBoundsException("Index:"+index+"Size:"+this.size);
        }
    }
    private Node getNode(int index){
        Node<E> node = this.head;
        for (int i=0;i<index;i++){
            node = node.next;
        }
        return node;
    }

    @Override
    public E remove(int index) {
        this.checkIndex(index);
        Node<E> node = this.getNode(index);
        E item = node.item;
        if(this.head == node){
            this.head = node.next;
        }else{
            Node<E> temp =this.head;
            for(int i=0;i<index-1;i++){
                temp = temp.next;
            }
            temp.next = node.next;
        }
        node.next = null;
        this.size--;
        return item;
    }

    @Override
    public int size() {
        return this.size;
    }

    public static void main(String[] args) {
        MySinglyLinkedList<String> mySinglyLinkedList = new MySinglyLinkedList<>();
        MySinglyLinkedList.add("a");
        MySinglyLinkedList.add("b");
        MySinglyLinkedList.add("c");
        MySinglyLinkedList.add("d");
        System.out.println(MySinglyLinkedList.size());

    }
}


JAVA 全系列/第二阶段:JAVA 基础深化和提高/数据结构 372楼

以下是我自己对UDP 一个发送者与一个接受者,多次咨询,双方互相发送的代码的优化,参考TCP的多线程

首先是Clinet项目

为一个发送者建立两个线程类,一个Send类,一个Receive类。用于发送和接收。

Send类

package com.gz.udp2;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Scanner;

public class Send implements Runnable{
    private DatagramSocket ds;
    private Scanner sc;
    private String destIp;//目标ip
    private int destPort;//目标端口号
    private String role;//角色名称
    private boolean flag=true;


    public Send(DatagramSocket ds,String destIp,int destPort,String role) {
        this.ds = ds;
        this.destIp=destIp;
        this.destPort=destPort;
        this.role=role;
        sc=new Scanner(System.in);
    }
    private String getMessage(){
        return sc.next();
    }
    private void send(String str){
        //发送时间
        String date=new Date().toLocaleString();
        str=date+"   "+role+":"+str;
        byte[] buf=str.getBytes();
        try {
            DatagramPacket dp=new DatagramPacket(buf,buf.length, InetAddress.getByName(destIp),destPort);
            ds.send(dp);
        } catch (IOException e) {
            flag=false;
            if(ds!=null) {
                ds.close();
            }
        }
    }

    @Override
    public void run() {
        while(flag){
            this.send(this.getMessage());
        }

    }
}

Receive类

package com.gz.udp2;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.Scanner;

public class Receive implements Runnable{

    private DatagramSocket ds;
    private Scanner sc;
    private boolean flag=true;

    public Receive(DatagramSocket ds) {
        this.ds = ds;
        sc=new Scanner(System.in);
    }
    private String receive(){
        String str="";
        try {
            byte [] buf=new byte[1024];//等待接收数据
            DatagramPacket dp=new DatagramPacket(buf,buf.length);
            ds.receive(dp);
            str=new String(dp.getData(),0,dp.getLength());
        } catch (IOException e) {
            flag=false;
            if(ds!=null){
                ds.close();
            }
        }
        return str;
    }

    @Override
    public void run() {
        while(flag){
            System.out.println(this.receive());
        }
    }
}

Test1类,开启线程的类

package com.gz.udp2;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

public class Test1 {
    public static void main(String[] args) throws IOException {
        //DS
        DatagramSocket ds=new DatagramSocket(9777);//自己的端口号

        Send s=new Send(ds,"localhost",9888,"咨询者");
        //目标ip,目标端口号,自己角色
        Receive r=new Receive(ds);

        new Thread(s).start();
        new Thread(r).start();


    }
}


对于接收者,也是拥有两个类,Send类和Receive类,及同样的开启线程类,只不过是端口号不同,角色不同。

接收者的Test2类

package com.gz.udp2;

import java.io.IOException;
import java.net.DatagramSocket;

public class Test2 {
    public static void main(String[] args) throws IOException {
        //DS
        DatagramSocket ds=new DatagramSocket(9888);

        Send s=new Send(ds,"localhost",9777,"客服人员");
        //目标ip,目标端口号,自己角色
        Receive r=new Receive(ds);

        new Thread(s).start();
        new Thread(r).start();


    }
}


结果:成功实现了,客服多次回复:又优化加上了发送时间:

咨询者端:

image.png

客服端:

image.png


想让老师看一下我的代码还有哪里值得优化,还有一个问题,就是,单对单的时候,接收者在回复的时候可以获得发送者发送来的ip与端口号,但是我现在写成了多线程,并且在我的发送者与接收者的测试类里,ip跟端口号是单独指定的,也就是说,当线程开启之前,ip与端口号就固定下来了,下面的交互都是这两个ip与端口号之间的交互。我想问的是有办法像单对单那样,获取到上一个接收过来的数据来自的ip和端口号吗,这样的话就要找到上一个接收线程的DatagramPacket数据包dp,从dp能够获取到上一个发送端的ip及端口号,可是由于多线程,我编辑的逻辑是,每次接收和发送的方法都会创建新的DatagramPacket,当上一个线程调用发送方法以后,随着方法结束,dp数据包也会随之在内存中释放消失。所以,我觉得是完不成根据dp获取上一个发送端的ip及端口号的。因此这也是我的发送端和接收端都要重新制定ip及端口号的原因。

思考到这,(小总结******)我在想UDP是不是就是适合于两个人在通信的时候进行的交互,比如qq聊天,客服聊天,并且这两个人的聊天是指定两个人的单次会话的多次聊天。

而TCP适合群聊,中间的服务器可以将信息转发给其他人,也可以满足随时有人加入群聊,为随时加入群聊的客户端开启处理线程。


以上是我的思考,不知道有没有误解,想跟老师求证最后说的总结(带******号的)的正确性。

还有上面多线程的代码是否可以进一步优化,以及我对我的程序中发送端和接收端都要重新制定ip及端口号的理解是否正确。

还有就是UDP的单次会话的多线程如果真的实现是不是像我写的那样的,发送端和接收端都要重新制定ip及端口号。

JAVA 全系列/第二阶段:JAVA 基础深化和提高/网络编程(旧) 373楼

一、如下代码

package cn.sxt.pool108;
 
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
 
public class Test02 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 如何创建一个线程池
        // 1.创建一个线程池,线程池中只有一个线程对象
         //ExecutorService pool = Executors.newSingleThreadExecutor();
        // 2.创建一个线程池,线程池中有线程的数量固定
         ExecutorService pool = Executors.newFixedThreadPool(10);
        // 3.创建一个线程池,线程池中的线程的数量可以动态的改变
        //ExecutorService pool = Executors.newCachedThreadPool();
         
        //创建一个集合
         List<Future> list = new ArrayList<Future>();
          
        /**使用线程池执行大量的Callable任务*/
        for(int i=0;i<20;i++) {
            //使用匿名内部类
            //创建任务
            Callable<Integer> task = new Callable<Integer>() {
 
                @Override
                public Integer call() throws Exception {
                    Thread.sleep(2000);
                    return (int)(Math.random()*10)+1;
                }              
            };//任务结束
            //将任务交给线程池
            Future f = pool.submit(task);
            //每执行完一个任务就将它添加到集合当中去
            list.add(f);
            //System.out.println(f.get());
        }
        System.out.println("ok?");
        //任务全部执行完毕之后再去遍历集合
        for (Future future : list) {
            System.out.println(future.get());
        }
        System.out.println("OKOK!");
        //关闭线程池
        pool.shutdown();
    }
}

老师,当我将任务交给线程池以后,打印出来f.get();这时候效率没有增加,一个个打印出来。当我放入集合中的时候,也是每次一个个放进来,我并没有觉得效率增加了啊。为什么有System.out.println(f.get());的时候后面的线程要等System.out.println(f.get());完成后再走,而有list.add(f)时,线程可以同步运行。这不应该是同级别的吗?

JAVA 全系列/第二阶段:JAVA 基础深化和提高/多线程和并发编程(旧) 374楼

一、问题:判断一个字符串的对称性。

老师,为啥我输出的会是false,感觉逻辑没有问题,而且代码跟老师讲课一样,但为什么输出的不是true。

二、代码

package com.bjsxt;

import java.util.Stack;

public class StackTest {
    public static void main(String[] args) {
        System.out.println("-------判断字符串的对称性---------");
        StackTest stackTest = new StackTest();
        stackTest.symmetry();
    }
    //判断字符串的对成型
    public void symmetry(){
        String str="...{.....[....(....)...]....}..(....)..[...]...";
        Stack<String> stack = new Stack<>();
        boolean flag = true;//假设是匹配的
        for (int i=0;i<str.length();i++){
            char c = str.charAt(i); //拆分字符,把字符拿出来
            if (c=='{'){
                stack.push("}");
            }
            if (c=='['){
                stack.push("]");
            }
            if (c=='('){
                stack.push(")");
            }
            if (c == ')'|| c == ']'|| c == '}'){
                //修正处理
                if (stack.empty()){
                    flag = false;
                    break;
                }
                String x = stack.pop();
                //修正处理
                if (x.charAt(0) != c){
                    flag = false;
                    break;
                }
            }
        }
        if (!stack.empty()){
            flag = false;
        }
        System.out.println(flag);
    }
}

三、运行效果图

图片.png

JAVA 全系列/第二阶段:JAVA 基础深化和提高/容器(旧) 375楼

课程分类

百战程序员微信公众号

百战程序员微信小程序

©2014-2025百战汇智(北京)科技有限公司 All Rights Reserved 北京亦庄经济开发区科创十四街 赛蒂国际工业园
网站维护:百战汇智(北京)科技有限公司
京公网安备 11011402011233号    京ICP备18060230号-3    营业执照    经营许可证:京B2-20212637