zoukankan      html  css  js  c++  java
  • 链表

    链表

    链表(Linked List)

    动态数组有个明显得缺点

    • 可能造成内存空间的大量浪费

    能否用到多少就申请多少内存?

    • 链表可以办到这一点

    链表是一种链式存储的线性表, 所有元素的内存地址不一定是连续的

    链表(Linked List)接口设计

    链表的大部分接口和动态数组是一致的

    int size();//元素的数量
    boolean isEmpty();//是否为空
    boolean contains(E element);//是否包含某个元素
    void add(E element);//添加元素到最后面
    E get(int index);//返回index位置对应的元素
    E set(int index, E element);//设置index位置的元素
    void add(int index, E element);//往index位置添加元素
    E remove(int index);//删除index位置对应的元素
    int indexOf(E element);//查看元素的位置
    void clear();//清除所有元素
    

    由于链表的大部分接口和动态数组一致,我们抽取出一个共同的 List 接口

    /**
     * list 接口供动态数组和链表实现
     * @param <E> 泛型
     */
    public interface List<E> {
        // 表示元素不存在
        static final int ELEMENT_NOT_FOUND = -1;
        /**
         * 清除所有元素
         */
        void clear();
    
        /**
         * 元素的数量
         * @return
         */
        int size();
    
        /**
         * 是否为空
         * @return
         */
        boolean isEmpty();
    
        /**
         * 是否包含某个元素
         * @param element
         * @return
         */
        boolean contains(E element);
    
        /**
         * 添加元素到尾部
         * @param element
         */
        void add(E element);
    
        /**
         * 获取index位置的元素
         * @param index
         * @return
         */
        E get(int index);
    
        /**
         * 设置index位置的元素
         * @param index
         * @param element
         * @return 原来的元素ֵ
         */
        E set(int index, E element);
    
        /**
         * 在index位置插入一个元素
         * @param index
         * @param element
         */
        void add(int index, E element);
    
        /**
         * 删除index位置的元素
         * @param index
         * @return
         */
        E remove(int index);
    
        /**
         * 查看元素的索引
         * @param element
         * @return
         */
        int indexOf(E element);
    }
    

    再将一些通用的字段与方法放到一个抽象类中,无论是动态数组还是链表只需要继承这个抽象类

    /**
     * 动态数组和链表公共的方法 其他方法有各自的实现
     * @param <E> 泛型
     */
    public abstract class AbstractList<E> implements List<E>{
        /**
         * 元素数量
         */
        protected int size;
    
        /**
         * 元素的数量
         * @return
         */
        public int size(){
            return size;
        }
        /**
         * 是否为空
         * @return
         */
        public boolean isEmpty(){
            return size == 0;
        }
        /**
         * 是否包含某个元素
         * @param element 元素
         * @return
         */
        public boolean contains(E element){
            return indexOf(element) != ELEMENT_NOT_FOUND;
        }
        /**
         * 添加元素到最后面
         * @param element
         */
        public void add(E element){
            add(size,element);
        }
    
        /**
         * 越界异常提示
         * @param index
         */
        protected void outOfBounds(int index){
            throw new IndexOutOfBoundsException("Index:"+index+", Size:"+size);
        }
    
        /**
         * 边界判断
         * @param index
         */
        protected void rangeCheck(int index){
            if (index < 0 || index >= size){
                this.outOfBounds(index);
            }
        }
    
        /**
         * 添加时边界判断
         * @param index
         */
        protected void rangeCheckForAdd(int index){
            if (index < 0 || index > size){
                this.outOfBounds(index);
            }
        }
    }
    
    创建 LinkedList 继承 AbstractList
    • size属性记录存储数据的数量,创建first属性引用链表的第0个元素,来管理链表的数据
    • 创建私有类Node,其中的element属性用于存储元素,next属性用于指向链表中的下一个结点
    public class LinkedList<E> extends AbstractList<E>{
        /**
         * 元素所在的结点
         */
        private Node<E> first;
    
        /**
         * 内部类 结点
         * @param <E>
         */
        private static class Node<E>{
            E element;//元素
            Node<E> next;//下个元素的地址
            public Node(E element, Node<E> next) {
                this.element = element;
                this.next = next;
            }
        }
    }
    
    默认构造方法

    链表的创建与动态数组不同,动态数组在构造时需要传入一个空间属性,来决定这个数组的容量。但链表元素是在添加时才创建的,内存地址不一定是连续的。所以链表不需要在单独设计构造方法,使用默认构造方法

    清空元素 - clear()

    first指向null,释放链表所有node,同时size置为0

    public class LinkedList<E> extends AbstractList<E>{
        ......
        
        /**
         * 清除所有元素
         */
        public void clear(){
            size = 0;
            first = null;
        }    
            
        ......
    }
    
    查找结点 - node(int index)

    此时需要添加node()方法获取索引(index)位置对应的结点

    public class LinkedList<E> extends AbstractList<E>{
        ......
        
         /**
         * 获取index位置对应的结点对象
         * @param index
         * @return
         */
        private Node<E> node(int index){
            rangeCheck(index);
            Node<E> node = first;
            for (int i = 0; i < index; i++) {
                node = node.next;
            }
            return node;
        }
            
        ......
    }
    
    添加元素 - add(int index, E element)
    • 添加数据结点时,需要创建一个结点存储数据,并将该结点拼接到最后结点的后面,然后size加1
    • 需要区分当前链表没有数据,新结点拼接到first当前链表有数据,新结点拼接到最后的结点
    • 添加元素尤其要注意 0 位置,给空链表添加first结点是个特殊情况

    public class LinkedList<E> extends AbstractList<E>{
        ......
        
         /**
         * 返回index位置对应的元素
         * @param index
         * @return
         */
        public E get(int index){
            return node(index).element;
        }
        /**
         * 设置index位置的元素
         * @param index
         * @param element
         * @return
         */
        public E set(int index, E element){
            Node<E> node = node(index);
            E old = node.element;
            node.element = element;
            return old;
        }
    
        /**
         * 往index位置添加元素
         * @param index
         * @param element
         */
        public void add(int index, E element){
            //边界判断
            rangeCheckForAdd(index);
            if (index == 0){// 给空链表添加第一个元素的情况
                //创建新结点,first结点指向这个新结点
                first = new Node<>(element,first);
            }else {
                //index上一个结点
                Node<E> prev = node(index - 1);
                //创建新结点,prev结点指向这个新结点
                prev.next = new Node<>(element, prev.next);
            }
            size++;
        }
            
        ......
    }
    
    删除元素 - remove(int index)
    • 首先找到删除结点前一个结点(prev),然后通过变更(prev)结点next指针指向删除结点的下一个结点,然后size减1
    • 需要判断是否删除的第0个元素,如果是,则使用first结点指向第1个结点

    public class LinkedList<E> extends AbstractList<E>{
        ......
        
         /**
         * 删除index位置对应的元素
         * @param index
         * @return
         */
        public E remove(int index){
            //边界判断
            rangeCheck(index);
            //记录结点
            Node<E> node = first;
            if (index == 0){ // 删除第一个元素是特殊情况
                first = first.next;
            }else {
                //index上一个结点
                Node<E> prev = node(index - 1);
                //记录删除的结点
                node = prev.next;
                //node结点下一个结点,prev结点指向这个结点
                prev.next = node.next;
            }
            size --;
            return node.element;
        }
            
        ......
    }
    
    查找元素位置 - indexOf(E element)
    • 查找指定元素的索引,需要遍历所有结点,找到结点对应的元素与执行元素相等

      如果需要支持结点element为null,则需要分情况处理

    public class LinkedList<E> extends AbstractList<E>{
        ......
        
         /**
         * 查看元素的位置
         * @param element
         * @return
         */
        public int indexOf(E element){
            // 取出头结点
            Node<E> node = first;
            if(element ==null){// 当element为null时的处理
                // 遍历结点, 找到存储为null的结点, 返回索引
                for (int i = 0; i < size; i++) {
                    if (node.element == null) return i;
                    node = node.next;
                }
            }else {
                // 遍历结点, 找到存储的元素与指定元素相等的结点, 返回索引
                for (int i = 0; i < size; i++) {
                    if (element.equals(node.element)) return i;
                    node = node.next;
                }
            }
    
            return ELEMENT_NOT_FOUND;
        }
            
        ......
    }
    
    打印元素 - toString()
    • 重写toString方法
    • toString 方法中将元素拼接成字符串
    • 字符串拼接建议使用StringBuilder
    public class LinkedList<E> extends AbstractList<E>{
        ......
        
         /**
         * 打印
         * @return
         */
        @Override
        public String toString() {
            //Size = ? , [1, 2, 3]
            StringBuilder string = new StringBuilder();
            string.append("size=").append(size).append(", [");
            Node<E> node = first;
            for (int i = 0; i < size; i++) {
                if (i != 0){
                    string.append(", ");
                }
                string.append(node.element);
                node = node.next;
            }
            string.append("]");
            return string.toString();
        }
            
        ......
    }
    
    完整代码 --- 单向链表
    /**
     * 单链表的实现
     * @param <E> 泛型
     */
    public class LinkedList<E> extends AbstractList<E>{
        /**
         * 元素所在的结点
         */
        private Node<E> first;
    
        /**
         * 内部类 结点
         * @param <E>
         */
        private static class Node<E>{
            E element;//元素
            Node<E> next;//下个元素的地址
            public Node(E element, Node<E> next) {
                this.element = element;
                this.next = next;
            }
        }
    
        /**
         * 返回index位置对应的元素
         * @param index
         * @return
         */
        public E get(int index){
            return node(index).element;
        }
        /**
         * 设置index位置的元素
         * @param index
         * @param element
         * @return
         */
        public E set(int index, E element){
            Node<E> node = node(index);
            E old = node.element;
            node.element = element;
            return old;
        }
    
        /**
         * 往index位置添加元素
         * @param index
         * @param element
         */
        public void add(int index, E element){
            //边界判断
            rangeCheckForAdd(index);
            if (index == 0){// 给空链表添加第一个元素的情况
                //创建新结点,first结点指向这个新结点
                first = new Node<>(element,first);
            }else {
                //index上一个结点
                Node<E> prev = node(index - 1);
                //创建新结点,prev结点指向这个新结点
                prev.next = new Node<>(element, prev.next);
            }
            size++;
        }
        /**
         * 删除index位置对应的元素
         * @param index
         * @return
         */
        public E remove(int index){
            //边界判断
            rangeCheck(index);
            //记录结点
            Node<E> node = first;
            if (index == 0){ // 删除第一个元素是特殊情况
                first = first.next;
            }else {
                //index上一个结点
                Node<E> prev = node(index - 1);
                //记录删除的结点
                node = prev.next;
                //node结点下一个结点,prev结点指向这个结点
                prev.next = node.next;
            }
            size --;
            return node.element;
        }
    
        /**
         * 查看元素的位置
         * @param element
         * @return
         */
        public int indexOf(E element){
            // 取出头结点
            Node<E> node = first;
            if(element ==null){// 当element为null时的处理
                // 遍历结点, 找到存储为null的结点, 返回索引
                for (int i = 0; i < size; i++) {
                    if (node.element == null) return i;
                    node = node.next;
                }
            }else {
                // 遍历结点, 找到存储的元素与指定元素相等的结点, 返回索引
                for (int i = 0; i < size; i++) {
                    if (element.equals(node.element)) return i;
                    node = node.next;
                }
            }
    
            return ELEMENT_NOT_FOUND;
        }
        /**
         * 清除所有元素
         */
        public void clear(){
            size = 0;
            first = null;
        }
    
        /**
         * 获取index位置对应的结点对象
         * @param index
         * @return
         */
        private Node<E> node(int index){
            rangeCheck(index);
            Node<E> node = first;
            for (int i = 0; i < index; i++) {
                node = node.next;
            }
            return node;
        }
    
        /**
         * 打印
         * @return
         */
        @Override
        public String toString() {
            //Size = ? , [1, 2, 3]
            StringBuilder string = new StringBuilder();
            string.append("size=").append(size).append(", [");
            Node<E> node = first;
            for (int i = 0; i < size; i++) {
                if (i != 0){
                    string.append(", ");
                }
                string.append(node.element);
                node = node.next;
            }
            string.append("]");
            return string.toString();
        }
    
    
    }
    
    
    虚拟头结点 --- 存在内存问题

    有时候为了让代码更加精简,统一所有结点的处理逻辑,可以在最前面增加一个虚拟头结点(不存储数据)

    带虚拟头结点的单向链表与普通单向链表代码类似:但是 addremove略有不同;

    /**
     * 增加一个虚拟头结点
     * @param <E> 泛型
     */
    public class VirtualHeadLinkedList<E> extends AbstractList<E>{
        /**
         * 元素所在的结点
         */
        private Node<E> first;
    
        /**
         * 内部类 结点
         * @param <E>
         */
        private static class Node<E>{
            E element;//元素
            Node<E> next;//下个元素的地址
            public Node(E element, Node<E> next) {
                this.element = element;
                this.next = next;
            }
        }
    
        /**
         * 构造函数 创建一个空结点 --- 增加
         */
        public VirtualHeadLinkedList() {
            this.first = new Node<>(null,null);
        }
    
        /**
         * 返回index位置对应的元素
         * @param index
         * @return
         */
        public E get(int index){
            return node(index).element;
        }
        /**
         * 设置index位置的元素
         * @param index
         * @param element
         * @return
         */
        public E set(int index, E element){
            Node<E> node = node(index);
            E old = node.element;
            node.element = element;
            return old;
        }
    
        /**
         * 往index位置添加元素 --- 修改
         * @param index
         * @param element
         */
        public void add(int index, E element){
            //边界判断
            rangeCheckForAdd(index);
            Node<E> prev = index ==0 ? first : node(index - 1);
            prev.next = new Node<>(element, prev.next);
            size++;
        }
        /**
         * 删除index位置对应的元素 --- 修改
         * @param index
         * @return
         */
        public E remove(int index){
            //边界判断
            rangeCheck(index);
            Node<E> prev = index == 0 ? first : node(index - 1);
            Node<E> node = prev.next;
            prev.next = node.next;
            size --;
            return node.element;
        }
    
        /**
         * 查看元素的位置
         * @param element
         * @return
         */
        public int indexOf(E element){
            // 取出头结点
            Node<E> node = first;
            if(element ==null){// 当element为null时的处理
                // 遍历结点, 找到存储为null的结点, 返回索引
                for (int i = 0; i < size; i++) {
                    if (node.element == null) return i;
                    node = node.next;
                }
            }else {
                // 遍历结点, 找到存储的元素与指定元素相等的结点, 返回索引
                for (int i = 0; i < size; i++) {
                    if (element.equals(node.element)) return i;
                    node = node.next;
                }
            }
    
            return ELEMENT_NOT_FOUND;
        }
        /**
         * 清除所有元素
         */
        public void clear(){
            size = 0;
            first = null;
        }
    
        /**
         * 获取index位置对应的结点对象 --- 修改
         * @param index
         * @return
         */
        private Node<E> node(int index){
            rangeCheck(index);
            //first的下一个结点
            Node<E> node = first.next;
            for (int i = 0; i < index; i++) {
                node = node.next;
            }
            return node;
        }
    
        /**
         * 打印 --- 修改
         * @return
         */
        @Override
        public String toString() {
            //Size = ? , [1, 2, 3]
            StringBuilder string = new StringBuilder();
            string.append("size=").append(size).append(", [");
            Node<E> node = first.next;
            for (int i = 0; i < size; i++) {
                if (i != 0){
                    string.append(", ");
                }
                string.append(node.element);
                node = node.next;
            }
            string.append("]");
            return string.toString();
        }
    
    }
    
    
    动态数组、链表复杂度分析

    数组的随机访问速度非常快:elements[n]的效率与 n 是多少无关;

    双向链表

    • 单向链表只能通过Node中next属性从头遍历链表,完成搜索
    • 双向链表中的Node增加prev属性,指向该结点上一个结点
    • 双向链表查找元素可以从first或last两个方向开始查找

    因此双向链表可以提升链表的综合性能

    双向链表只有一个结点(元素)

    双向链表接口设计

    相较于单向链表,双向链表需要重写查找结点插入结点删除结点清空结点四个方法

    • 新增一个last元素,最后一个元素
    • 私有结点Node 中的prev 元素用于指向链表中的上一个结点
    public class LinkedList<E> extends AbstractList<E> {
        /**
         * 元素所在的结点
         */
        private Node<E> first;
         //增加last结点
        private Node<E> last;
    
        /**
         * 内部类 结点
         * @param <E>
         */
        private static class Node<E>{
            E element;//元素
            Node<E> prev;//增加上一个元素地址
            Node<E> next;//下个元素的地址
            public Node(E element, Node<E> prev,Node<E> next) {
                this.element = element;
                this.prev = prev;
                this.next = next;
            }
        }
    }
    
    查找结点 - node(int index)

    通过判断查找的节点在链表的前半段或后半段,选择first结点从前往后遍历或last结点从后往前遍历

    public class LinkedList<E> extends AbstractList<E>{
        ......
        
        /**
         * 获取index位置对应的节点对象
         * @param index
         * @return
         */
        private Node<E> node(int index){
            rangeCheck(index);
            //往后
            if (index < (size >> 1)){
                Node<E> node = first;
                for (int i = 0; i < index; i++) {
                    node = node.next;
                }
                return node;
            }else {//往前
                Node<E> node = last;
                for (int i = size - 1; i > index; i--) {
                    node = node.prev;
                }
                return node;
            }
        }
            
        ......
    }
    
    清空元素 - clear()

    需将新增属性last置为null

    public class LinkedList<E> extends AbstractList<E>{
        ......
        
        /**
         * 清除所有元素
         */
        public void clear(){
            size = 0;
            first = null;
            last = null;
        }
            
        ......
    }
    
    添加元素 - add(int index, E element)
    • 假定插入位置原结点为oldNode(下图2号结点),上一个结点为prevNode(下图1号结点),新结点为node

    • prevNodenext现在为node

    • oldNodeprev现在为node

    • nodeprev即为oldNodeprev

    • nodenextoldNode

    • 特殊情况,插入在最前面:

      • 如果oldNodeprev为null,那么oldNode为原头结点。那么双向链表的first属性需改为node
    • 插入在最后面:

      • nodeprev为双向链表的lastnextnull,同时last需改为node
      • 如果原链表为空,则双向链表的firstlast都为node

    public class LinkedList<E> extends AbstractList<E>{
        ......
        
        /**
         * 往index位置添加元素
         * @param index
         * @param element
         */
        public void add(int index, E element){
            //边界判断
            rangeCheckForAdd(index);
            // size == 0
            // index == 0
            if (index == size){//往最后添加元素
                Node<E> oldLast = last;
                last = new Node<>(element,oldLast,null);
                if (oldLast == null){//这是链表的第一个元素
                    first = last;
                }else {
                    oldLast.next = last;
                }
            }else {// 正常添加元素
                //插入位置的原结点,即为新节点的next节点
                Node<E> next = node(index);
                //新添加结点的上一个节点,即为该位置原节点的上一个节点
                Node<E> prev = next.prev;
                //创建新结点
                Node<E> node = new Node<>(element,prev,next);
                //原结点的上一个结点,为新添加结点
                next.prev = node;
                if (prev == null){//index == 0 即添加的是第一个
                    first = node;
                }else {
                    //原结点上一个结点的next,即为新添加节点
                    prev.next = node;
                }
            }
    
            size++;
        }
            
        ......
    }
    
    删除元素 - remove(int index)
    • 删除结点, 只需要让被删除结点的前一个结点与后一个结点之间链接, 同时去掉被删除结点引用
    • 需要注意的是, 第0个结点和最后一个结点要特殊处理
    public class LinkedList<E> extends AbstractList<E>{
        ......
        
        /**
         * 删除index位置对应的元素
         * @param index
         * @return
         */
        public E remove(int index){
            //边界判断
            rangeCheck(index);
            //删除的结点
            Node<E> node = node(index);
            //删除结点的上一个结点
            Node<E> prev = node.prev;
            //删除结点的下一个结点
            Node<E> next = node.next;
            //删除结点, 只需去掉对这个结点的引用
            if (prev == null){//index == 0 即删除的是第一个
                first = next;
            }else {
                prev.next = next;
            }
            if (next == null){//index == size - 1 即删除的是最后一个
                last = prev;
            }else {
                next.prev = prev;
            }
            size --;
            return node.element;
        }
            
        ......
    }
    
    完整代码 --- 双向链表
    /**
     * 双向链表的实现
     * @param <E> 泛型
     */
    public class LinkedList<E> extends AbstractList<E> {
        /**
         * 元素所在的结点
         */
        private Node<E> first;
        //增加last节点
        private Node<E> last;
    
        /**
         * 内部类 结点
         * @param <E>
         */
        private static class Node<E>{
            E element;//元素
            Node<E> prev;//增加上一个元素地址
            Node<E> next;//下个元素的地址
            public Node(E element, Node<E> prev,Node<E> next) {
                this.element = element;
                this.prev = prev;
                this.next = next;
            }
        }
    
        /**
         * 返回index位置对应的元素
         * @param index
         * @return
         */
        public E get(int index){
            return node(index).element;
        }
        /**
         * 设置index位置的元素
         * @param index
         * @param element
         * @return
         */
        public E set(int index, E element){
            Node<E> node = node(index);
            E old = node.element;
            node.element = element;
            return old;
        }
    
        /**
         * 往index位置添加元素
         * @param index
         * @param element
         */
        public void add(int index, E element){
            //边界判断
            rangeCheckForAdd(index);
            // size == 0
            // index == 0
            if (index == size){//往最后添加元素
                Node<E> oldLast = last;
                last = new Node<>(element,oldLast,null);
                if (oldLast == null){//这是链表的第一个元素
                    first = last;
                }else {
                    oldLast.next = last;
                }
            }else {// 正常添加元素
                //插入位置的原结点,即为新节点的next节点
                Node<E> next = node(index);
                //新添加结点的上一个节点,即为该位置原节点的上一个节点
                Node<E> prev = next.prev;
                //创建新结点
                Node<E> node = new Node<>(element,prev,next);
                //原结点的上一个结点,为新添加结点
                next.prev = node;
                if (prev == null){//index == 0 即添加的是第一个
                    first = node;
                }else {
                    //原结点上一个结点的next,即为新添加节点
                    prev.next = node;
                }
            }
    
            size++;
        }
        /**
         * 删除index位置对应的元素
         * @param index
         * @return
         */
        public E remove(int index){
            //边界判断
            rangeCheck(index);
            //删除的结点
            Node<E> node = node(index);
            //删除结点的上一个结点
            Node<E> prev = node.prev;
            //删除结点的下一个结点
            Node<E> next = node.next;
            //删除结点, 只需去掉对这个结点的引用
            if (prev == null){//index == 0 即删除的是第一个
                first = next;
            }else {
                prev.next = next;
            }
            if (next ==null){//index == size - 1 即删除的是最后一个
                last = prev;
            }else {
                next.prev = prev;
            }
            size --;
            return node.element;
        }
    
        /**
         * 查看元素的位置
         * @param element
         * @return
         */
        public int indexOf(E element){
            // 取出头结点
            Node<E> node = first;
            if(element ==null){// 当element为null时的处理
                // 遍历节点, 找到存储为null的节点, 返回索引
                for (int i = 0; i < size; i++) {
                    if (node.element == null) return i;
                    node = node.next;
                }
            }else {
                // 遍历节点, 找到存储的元素与指定元素相等的节点, 返回索引
                for (int i = 0; i < size; i++) {
                    if (element.equals(node.element)) return i;
                    node = node.next;
                }
            }
    
            return ELEMENT_NOT_FOUND;
        }
        /**
         * 清除所有元素
         */
        public void clear(){
            size = 0;
            first = null;
            last = null;
        }
    
        /**
         * 获取index位置对应的节点对象
         * @param index
         * @return
         */
        private Node<E> node(int index){
            rangeCheck(index);
            //往后
            if (index < (size >> 1)){
                Node<E> node = first;
                for (int i = 0; i < index; i++) {
                    node = node.next;
                }
                return node;
            }else {//往前
                Node<E> node = last;
                for (int i = size - 1; i > index; i--) {
                    node = node.prev;
                }
                return node;
            }
        }
    
        /**
         * 打印
         * @return
         */
        @Override
        public String toString() {
            //Size = ? , [1, 2, 3]
            StringBuilder string = new StringBuilder();
            string.append("size=").append(size).append(", [");
            Node<E> node = first;
            for (int i = 0; i < size; i++) {
                if (i != 0){
                    string.append(", ");
                }
                string.append(node.element);
                node = node.next;
            }
            string.append("]");
            return string.toString();
        }
    }
    
    

    双向链表和单向链表对比

    粗略对比一下删除的操作数量:操作数量缩减了近一半

    有了双向链表,单向链表是否就没有任何用处了?

    • 并非如此,在哈希表的设计中就用到了单链表
    • 至于原因,在哈希表中会详细讲解

    双向链表 vs 动态数组

    动态数组:开辟、销毁内存空间的次数相对较少,但可能造成内存空间浪费(可以通过缩容解决)
    双向链表:开辟、销毁内存空间的次数相对较多,但不会造成内存空间的浪费

    • 如果频繁在尾部进行添加、删除操作,动态数组、双向链表均可选择
    • 如果频繁在头部进行添加、删除操作,建议选择使用双向链表
    • 如果有频繁的 (在任意位置)添加、删除操作,建议选择使用双向链表
    • 如果有频繁的查询操作(随机访问操作),建议选择使用动态数组
  • 相关阅读:
    原生JS发送Ajax请求、JSONP
    python Flask中html模版中如何引用css,js等资源
    python 单例与数据库连接池 及相关选择
    费曼致学生真野光一
    Winform判断EventHandler是否已经添加
    The list of pre-build cross-compiler
    Extended VM Disk In VirtualBox or VMware (虚拟机磁盘扩容)
    一张图了解SSH端口转发
    显卡驱动卸载清理工具
    Oracle分析函数
  • 原文地址:https://www.cnblogs.com/netu/p/14999189.html
Copyright © 2011-2022 走看看