2023/7/12·数据结构之简单构造 ArrayList 和 LinkedList

1 · ArrayList的构造

1.1·原理

ArrayList底层就是一个数组,所以这里要还原一个ArrayList的话,就要在类中定义一个数组,当然这里不考虑泛型之类的,只是简单的做一下练习。

public class MyArrayList {
    private Object[] arr;
}

1.2·方法

1.2.1·添加

然后添加第一个方法:添加方法add()

 /**
     * 添加
     *
     * @param obj 添加的变量
     */
    public void add(Object obj) {
        if (arr == null) {  //判断是否为第一次添加
            arr = new Object[1];
            arr[0] = obj;
        } else {
            Object[] newArr = new Object[arr.length + 1];   //定义用于替换的新数组
            for (int i = 0; i < newArr.length; i++) {   //基于新数组长度遍历赋值
                if (i != arr.length) {  //如果不是新数组的最后一个元素
                    newArr[i] = arr[i]; //直接对应赋值
                } else {
                    newArr[i] = obj;    //否则将最后一个元素赋值为添加的元素
                }
            }
            arr = newArr;       //替换原数组
        }
    }

1.2.2·遍历和集合大小

当然少不了测试用的show() 方法,以及之后会用到的size() 方法

	public void show() {
        for (Object obj : arr) {
            System.out.print(obj + " ");
        }
    }
    /**
     * 集合大小
     * @return 返回成员数
     */
    public int size() {
        return arr.length;
    }

1.2.3·获取值

获取值的get() 方法

    /**
     * 获取值
      * @param index 索引
     * @return  返回的值
     */
    public Object get(int index) {
        if (index < 0 || index >= arr.length) {
            System.out.println("没有该索引!");
            return null;
        }
        return arr[index];
    }

1.2.4·修改

修改值的set() 方法

    /**
     * 修改
     *
     * @param index  索引
     * @param newObj 新变量
     */
    public void set(int index, Object newObj) {
        if (index < 0 || index >= arr.length) {
            System.out.println("没有该索引!");
            return;
        }
        arr[index] = newObj;
    }

1.2.5·移除

最后是移除的remove() 方法

    /**
     * 移除(按索引)
     *
     * @param index 索引
     */
    public void remove(int index) {
        if (index < 0 || index >= arr.length) {     //判断索引是否合法
            System.out.println("没有该索引!");
            return;
        }
        Object[] newArr = new Object[arr.length - 1];   //定义用于替换的新数组
        for (int i = 0; i < newArr.length; i++) {   //基于新数组长度遍历赋值
            if (i < index) {        //如果新数组的索引小于要移除的索引
                newArr[i] = arr[i]; //则对应赋值
            } else {
                newArr[i] = arr[i + 1]; //否则错位赋值,索引为原数组对应索引+1
            }
        }
        arr = newArr;   //替换原数组
    }

1.3·测试

这样一个ArrayList 就基本构造完成了,简单测试下:

        MyArrayList myArrayList = new MyArrayList();
        myArrayList.add(1);
        myArrayList.add(2);
        myArrayList.add(3);
        myArrayList.add(4);
        System.out.print("遍历:");
        myArrayList.show();
        myArrayList.set(0,22);
        System.out.print("\n设置新值后遍历:");
        myArrayList.show();
        System.out.println("\n索引为1的值:"+myArrayList.get(1));
        myArrayList.remove(1);
        System.out.println("集合大小:" + myArrayList.size());
        System.out.print("移除索引为1的元素后遍历:");
        myArrayList.show();
        //控制台输出:
        //遍历:1 2 3 4 
		//设置新值后遍历:22 2 3 4 
		//索引为1的值:2
		//集合大小:3
		//移除索引为1的元素后遍历:22 3 4 

2 · LinkedList(单向)的构造

2.1·原理

链表的每个数据存储单元就是一个节点Node, 这里没用内部类,而是用一个公共类演示,链表的类中只有一个head属性,由头部节点开始,每个节点存储有下一个节点的属性next,最后一个节点的next的值就为null。

public class Node {
    public Object value;
    public Node next;
}
public class MyLinkedList {
    private Node head;
}

2.2·方法

2.2.1·添加

添加方法add() ,这里单向链表就做了个尾部添加

     /**
     * 添加
     * @param obj 新增的值
     */
    public void add(Object obj) {
        Node node = new Node();     //创建新节点
        node.value = obj;           //节点赋值
        if (head == null) {         //判断是否为首次添加
            head = node;            //头节点定为新创建的节点
        } else {
            Node temp = head;       //存储头节点为循环指针
            while (temp.next != null) {     //判断指针的next属性是否为空
                temp = temp.next;       //不为空则将指针赋值为下一个节点
            }   
            temp.next = node;       //最后把找到的最后节点的next属性赋值新节点
        }
    }

2.2.2·获取节点和集合大小

获取节点的方法 get() ,这里需要另一个方法size() 来获取集合大小,用于判断索引是否越界

    /**
     * 获取
     * @param index 获取值的索引
     * @return  返回的节点
     */
    public Node get(int index) {
        if (head == null || index < 0 || index >= size()) { //判断head是否为空或者索引是否超出集合范围
            Node error = new Node();    
            error.value = null;
            return error;   //返回空节点
        }
        int i = 0;          //计数器
        Node temp = head;   
        while (temp.next != null) {
            if (i == index)     //若计数器等于索引,则找到对应节点,跳出循环
                break;
            temp = temp.next;
            i++;
        }
        return temp;
    }
    public int size() {
        if (head == null) {
            return 0;
        }
        int size = 1;
        Node temp = head;
        while (temp.next != null) {
            temp = temp.next;
            size++;
        }
        return size;
    }

2.2.3·移除

移除的方法remove()

    /**
     * 移除
     * @param index 移除的索引
     */
    public void remove(int index) {
        if (head == null || index < 0 || index >= size()) { //判断head是否为空或者索引是否超出集合范围
            System.out.println("索引越界!");
            return;
        }
        if (index == 0) {       //判断移除的是否为头节点
            head = head.next;       //把头节点的下一个节点设置为头节点,原来的头节点就与链表失去关联了
            return;
        }
        int i = 0;
        Node temp = head;
        while (temp.next != null) {
            if (index - 1 == i) {       //找到索引所在节点的上一个节点
                temp.next = temp.next.next; //把该节点的next设置为下一个节点的下一个节点,即为索引所在节点的下一个节点
                return;             
            }
            temp = temp.next;       
            i++;
        }
    }
    /**
     * 测试用遍历方法
     */
    public void show() {
        if (head == null) {
            System.out.println("链表为空");
            return;
        }
        Node temp = head;
        System.out.print(head.value + " ");
        while (temp.next != null) {
            temp = temp.next;
            System.out.print(temp.value + " ");
        }
    }
}

2.3·测试

这里简单测试下

        MyLinkedList myList = new MyLinkedList();
        myList.add(1);
        myList.add(2);
        myList.add(3);
        myList.add(4);
        myList.remove(3);
        myList.get(2).value = 4;
        myList.show();
        System.out.println("\n" + myList.get(3).value);
        System.out.println("集合大小:" +myList.size());
		//1 2 4 
		//null
		//集合大小:3

3 · 双向链表

3.1·原理

原理和单向列表类似,不过节点多了个prev属性,即前一个节点,链表类里也有两个属性:头head和尾tail

public class DoubleNode {
    public Object value;
    public DoubleNode prev;
    public DoubleNode next;

    public DoubleNode() {
    }

    public DoubleNode(Object value) {
        this.value = value;
    }
}
public class MyDoubleLinkedList {
    private DoubleNode head;
    private DoubleNode tail;
}

3.2·方法

3.2.1·头插和尾插

添加方法分为头插addFirst()和尾插addLast()

    /**
     * 头插
     * @param obj 值
     */
    public void addFirst(Object obj) {
        DoubleNode node = new DoubleNode(obj);  //创建新节点
        if (head == null) {         //判断集合是否为空,为空则使新节点为头和尾
            head = node;
            tail = node;
            return;
        }
        node.next = head;       //集合不为空的情况,把新节点的next指向原头节点
        head.prev = node;       //再把头节点的prev指向新节点
        head = node;            //最后把头节点替换为新节点
    }

    /**
     * 尾插
     * @param obj 值
     */
    public void addLast(Object obj) {
        DoubleNode node = new DoubleNode(obj);
        if (head == null) {
            head = node;
            tail = node;
            return;
        }
        tail.next = node;   //集合不为空的情况,把尾节点的next指向新节点
        node.prev = tail;   //再把新节点的prev指向原尾节点
        tail = node;        //最后把尾节点替换为新节点
    }

3.2.2·获取节点和集合大小

获取方法get() 和获取集合大小的size()

    /**
     * 获取
     * @param index 获取的索引
     * @return  返回的节点
     */
    public DoubleNode get(int index){
        if (head == null || index < 0 || index >= size()) { //判断head是否为空或者索引是否超出集合范围
            System.out.println("索引越界!");
            return null;   //返回空
        }
        int i = 0;          //计数器
        DoubleNode temp = head;
        while (temp.next != null) {
            if (i == index)     //若计数器等于索引,则找到对应节点,跳出循环
                break;
            temp = temp.next;
            i++;
        }
        return temp;
    }
    public int size() {
        int size = 0;
        DoubleNode temp = head;
        while (temp != null) {
            size++;
            temp = temp.next;
        }
        return size;
    }

3.2.3·移除

移除方法remove()

    /**
     * 移除
     * @param index 移除的索引
     */
    public void remove(int index) {
        if (head == null || index < 0 || index >= size()) { //判断head是否为空或者索引是否超出集合范围
            System.out.println("索引越界!");
            return;
        }
        if (index == 0) {       //判断移除的是否为头节点
            head.next.prev = null;  //把头节点的下一个节点的prev指向null
            head = head.next;       //把头节点的下一个节点设置为头节点,原来的头节点就与链表失去关联了
            return;
        }
        int i = 0;
        DoubleNode temp = head;
        while (temp.next != null) {
            if (index - 1 == i) {       //找到索引所在节点的上一个节点
                temp.next.next.prev = temp; //把该节点下一个节点的下一个节点的prev指向该节点
                temp.next = temp.next.next; //把该节点的next指向下一个节点的下一个节点,即为索引所在节点的下一个节点
                return;
            }
            temp = temp.next;
            i++;
        }
    }

3.2.4·两种方向的遍历

遍历的方法displayFromHead()displayFromTail()

    /**
     * 从头部遍历
     */
    public void displayFromHead() {
        DoubleNode temp = head;
        while (temp != null) {
            System.out.print(temp.value +" ");
            temp = temp.next;
        }
        System.out.println();
    }
    /**
     * 从尾部遍历
     */
    public void displayFromTail() {
        DoubleNode temp = tail;
        while (temp != null) {
            System.out.print(temp.value +" ");
            temp = temp.prev;
        }
        System.out.println();
    }

3.3·测试

        MyDoubleLinkedList myDoubleList = new MyDoubleLinkedList();
        myDoubleList.addLast(1);
        myDoubleList.addLast(2);
        myDoubleList.addLast(3);
        myDoubleList.addLast(4);
        myDoubleList.addLast(5);
        myDoubleList.addFirst(6);
        myDoubleList.addLast(7);
        System.out.print("集合元素(从头部遍历):");
        myDoubleList.displayFromHead();
        System.out.println("get方法:"+myDoubleList.get(6).value);
        System.out.println("集合大小:"+myDoubleList.size());
        System.out.print("集合元素:");
        myDoubleList.displayFromHead();
        myDoubleList.remove(0);
        System.out.print("移除后集合元素(从头部遍历):");
        myDoubleList.displayFromHead();
        System.out.print("移除后集合元素(从尾部遍历):");
        myDoubleList.displayFromTail();
        System.out.println("集合大小:"+myDoubleList.size());
//        集合元素(从头部遍历):6 1 2 3 4 5 7
//        get方法:7
//        集合大小:7
//        集合元素:6 1 2 3 4 5 7
//        移除后集合元素(从头部遍历):1 2 3 4 5 7
//        移除后集合元素(从尾部遍历):7 5 4 3 2 1
//        集合大小:6

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值