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 动态数组

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

    • 如果频繁在尾部进行添加、删除操作,动态数组、双向链表均可选择
    • 如果频繁在头部进行添加、删除操作,建议选择使用双向链表
    • 如果有频繁的 (在任意位置)添加、删除操作,建议选择使用双向链表
    • 如果有频繁的查询操作(随机访问操作),建议选择使用动态数组
  • 相关阅读:
    算法竞赛入门经典习题2-3 韩信点兵
    ios入门之c语言篇——基本函数——5——素数判断
    ios入门之c语言篇——基本函数——4——数值交换函数
    144. Binary Tree Preorder Traversal
    143. Reorder List
    142. Linked List Cycle II
    139. Word Break
    138. Copy List with Random Pointer
    137. Single Number II
    135. Candy
  • 原文地址:https://www.cnblogs.com/netu/p/14999189.html
Copyright © 2011-2022 走看看