数据结构与算法 ---- 线性表 及Java实现 顺序表、链表、栈、队列

2023-10-26

数据结构与算法是程序设计的两大基础,大型的IT企业面试时也会出数据结构和算法的题目,

它可以说明你是否有良好的逻辑思维,如果你具备良好的逻辑思维,即使技术存在某些缺陷,面试公司也会认为你很有培养价值,至少在一段时间之后,技术可以很快得到提高。同时,它也是软考的重点,我们需要对这部分的内容进行一下总结。

       我们先看一下数据结构和算法的整体内容。

                                        


1、线性表

       概念:

             数据元素的排列方式是线性的。

      分类:

             分类规则是根据上图中元素的存储结构来划分的。

                                                                                   

      (1)顺序表

          基本思想:元素的存储空间是连续的。在内存中是以顺序存储,内存划分的区域是连续的。存储结构如下图:

                           

          

      (2)链表

          基本思想:元素的存储空间是离散的,单独的(物理),它们可以通过在逻辑上指针的联系使得它成为了整体的链表。存储结构如下图:

                                


     

            1.单链表

                   


            2.循环链表

                        ·


            3.双链表(双向循环表)

                   

                 (图有点小问题 :最后一个节点的 指针域 也指向头结点)


           

           三者的区别(从上面三个图我们可以总结出来):

       1、它们都有数据域(data(p))和指针域(next(p)),但是从图中可以看出双链表有两个指针域,一个指向它的前节点,一个指向它的后节点。

       2、单链表最后一个节点的指针域为空,没有后继节点;循环链表和双链表最后一个节点的指针域指向头节点,下一个结点为头节点,构成循环;

          3、单链表和循环链表只可向一个方向遍历;双链表和循环链表,首节点和尾节点被连接在一起,可视为“无头无尾”;双链表可以向两个方向移动,灵活度更大。


    线性表操作:

         理解了顺序表和链表的基本思想之后,线性表的操作是简单,并且网上有很多讲解插入和删除结点的博客,在这里我就不过多的介绍了。


        顺序表和链表的对比:

                          


          栈和队列是特殊的线性表,既然特殊就有不同点。


2、栈

      基本思想:后进先出(先进后出)栈中元素被处理时,按后进先出的顺序进行,栈又叫后进先出表(LIFO)

      举例:

      日常生活中有很多栈的例子。例如,放在书桌上的一摞书,只能从书顶上拿走一本书,书也只能放在顶上。如下图所示:

                                                 


3、队列

     基本思想:先进先出即先被接收的元素将先被处理,又叫先进先出表(FIFO)。如下图所示:

     举例:

     队列的例子,生活中更多。比如:买车票排队,排头最先买到车票,新来的排的队尾;进车站时,安检行李,先进去的最先出来,后进去的后出来。

                                                  

   

分类:

1.顺序队列

          如下图所示:

                          

      顺序队列的操作,要判断队满和队空的标志,从图中我们可以总结得到:

      1.队空:head = tail

      2.队满:tail = m

2.循环队列

        如下图所示:

                                  

      循环队列的操作,要判断队空和队满的情况,从图中我们可以总结得到:

         1.队空:head = tail

       2.队满:tail + 1 = head(在队列中会留一个空着的空间,所以要加1)


总结

           线性表真的很简单。


----------------------------------------------------------------------------------------------------


数据结构中的线性表,对应着Collection接口中的List接口。

      在本节中,我们将做以下三件事

            第一。我们先来看看线性表的特征

            第二,自己用JAVA实现List

            第三,对比的线性表、链式表性能,以及自己的List性能与JDKList性能对比

 

      线性表特征: 

            第一,一个特定的线性表,应该是用来存放特定的某一个类型的元素的(元素的“同一性”)

            第二, 除第一个元素外,其他每一个元素有且仅有一个直接前驱;除最后一个元素外,其他每一个元素有且仅有一个直接后继(元素的“序偶性”)

            第三, 元素在线性表中的“下标”唯一地确定该元素在表中的相对位置(元素的“索引性”)

     又,一.线性表只是数据的一种逻辑结构,其具体存储结构可以为顺序存储结构和链式储存结构来完成,对应可以得到顺序表和链表,

            二.对线性表的入表和出表顺序做一定的限定,可以得到特殊的线性表,栈(FILO)和队列(FIFO)

 

    自己实现线性表之顺序表

             思路:

                1. 顺序表因为采用顺序存储形式,所以内部使用数组来存储数据

                2.因为存储的具体对象类型不一定,所以采用泛型操作

                3.数组操作优点:1.通过指针快速定位到下表,查询快速

                                 缺点:1.数组声明时即需要确定数组大小。当操作中超过容量时,则需要重新声明数组,并且复制当前所有数据

                                            2.当需要在中间进行插入或者删除时,则需要移动大量元素(size-index个)

  具体实现代码如下

Java代码
  1. /** 
  2.  * 自己用数组实现的线性表 
  3.  */  
  4. public class ArrayList<E> {  
  5.     Object[] data = null;// 用来保存此队列中内容的数组  
  6.     int current;        // 保存当前为第几个元素的指标  
  7.     int capacity;        // 表示数组大小的指标  
  8.        
  9.     /** 
  10.      * 如果初始化时,未声明大小,则默认为10 
  11.      */  
  12.     public ArrayList() {  
  13.         this(10);  
  14.     }  
  15.   
  16.     /** 
  17.      * 初始化线性表,并且声明保存内容的数组大小 
  18.      * @param initalSize 
  19.      */  
  20.     public ArrayList(int initalSize) {  
  21.         if (initalSize < 0) {  
  22.             throw new RuntimeException("数组大小错误:" + initalSize);  
  23.         } else {  
  24.             this.data = new Object[initalSize];  
  25.             this.current = 0;  
  26.             capacity = initalSize;  
  27.         }  
  28.     }  
  29.   
  30.     /** 
  31.      * 添加元素的方法 添加前,先确认是否已经满了 
  32.      * @param e 
  33.      * @return 
  34.      */  
  35.     public boolean add(E e) {  
  36.         ensureCapacity(current);// 确认容量  
  37.         this.data[current] = e;  
  38.         current++;  
  39.         return true;  
  40.     }  
  41.   
  42.     /** 
  43.      * 确认系统当前容量是否满足需要,如果满足,则不执行操作 如果不满足,增加容量 
  44.      * @param cur 当前个数 
  45.      */  
  46.     private void ensureCapacity(int cur) {  
  47.         if (cur == capacity) {  
  48.             // 如果达到容量极限,增加10的容量,复制当前数组  
  49.             this.capacity = this.capacity + 10;  
  50.             Object[] newdata = new Object[capacity];  
  51.             for (int i = 0; i < cur; i++) {  
  52.                 newdata[i] = this.data[i];  
  53.             }  
  54.             this.data = newdata;  
  55.         }  
  56.     }  
  57.   
  58.     /** 
  59.      * 得到指定下标的数据 
  60.      * @param index 
  61.      * @return 
  62.      */  
  63.     public E get(int index) {  
  64.         validateIndex(index);  
  65.         return (E) this.data[index];  
  66.     }  
  67.        
  68.    /** 
  69.     * 返回当前队列大小
  70.     * @return 
  71.     */  
  72.     public int size() {  
  73.         return this.current;  
  74.     }  
  75.   
  76.     /** 
  77.      * 更改指定下标元素的数据为e 
  78.      * @param index  
  79.      * @param e 
  80.      * @return 
  81.      */  
  82.     public boolean set(int index, E e) {  
  83.         validateIndex(index);  
  84.         this.data[index] = e;  
  85.         return true;  
  86.     }  
  87.      
  88.     /** 
  89.      *  验证当前下标是否合法,如果不合法,抛出运行时异常 
  90.      * @param index 下标 
  91.      */  
  92.     private void validateIndex(int index) {  
  93.         if (index < 0 || index > current) {  
  94.             throw new RuntimeException("数组index错误:" + index);  
  95.         }  
  96.     }  
  97.   
  98.     /** 
  99.      * 在指定下标位置处插入数据e 
  100.      * @param index 下标 
  101.      * @param e 需要插入的数据 
  102.      * @return  
  103.      */  
  104.     public boolean insert(int index, E e) {  
  105.         validateIndex(index);  
  106.         Object[] tem = new Object[capacity];// 用一个临时数组作为备份  
  107.         //开始备份数组  
  108.         for (int i = 0; i < current; i++) {  
  109.             if (i < index) {  
  110.                 tem[i] = this.data[i];  
  111.             }else if(i==index){  
  112.                 tem[i]=e;  
  113.             }else if(i>index){  
  114.                 tem[i]=this.data[i-1];  
  115.             }  
  116.         }  
  117.         this.data=tem;  
  118.         return true;  
  119.     }  
  120.      /**  * 删除指定下标出的数据<br>    
  121.       * @param index<br>  
  122.       * @return<br>   
  123.       */
  124.      public boolean delete(int index){
  125.           validateIndex(index);
  126.           Object[] tem = new Object[capacity];// 用一个临时数组作为备份
  127.           //开始备份数组
  128.          for (int i = 0; i < current; i++) { 
  129.                 if (i < index) {
  130.                     tem[i] = this.data[i];
  131.                 }else if(i==index){
  132.                     tem[i]=this.data[i+1];
  133.                 }else if(i>index){
  134.                     tem[i]=this.data[i+1];
  135.                 }
  136.             }
  137.           this.data=tem;
  138.           return true;
  139.      }
  140. }  

 

   自己实现线性表之链表

         思路:1.链表采用链式存储结构,在内部只需要将一个一个结点链接起来。(每个结点中有关于此结点下一个结点的引用)

         链表操作优点:1.因为每个结点记录下个结点的引用,则在进行插入和删除操作时,只需要改变对应下标下结点的引用即可

                       缺点:1.要得到某个下标的数据,不能通过下标直接得到,需要遍历整个链表。

  实现代码如下

Java代码   收藏代码
  1. /** 
  2.  * 自己用链式存储实现的线性表 
  3.  */  
  4. public class LinkedList<E> {  
  5.   
  6.     private Node<E> header = null;// 头结点  
  7.     int size = 0;// 表示数组大小的指标  
  8.   
  9.     public LinkedList() {  
  10.         this.header = new Node<E>();  
  11.     }  
  12.   
  13.     public boolean add(E e) {  
  14.         if (size == 0) {  
  15.             header.e = e;  
  16.         } else {  
  17.             // 根据需要添加的内容,封装为结点  
  18.             Node<E> newNode = new Node<E>(e);  
  19.             // 得到当前最后一个结点  
  20.             Node<E> last = getNode(size-1);  
  21.             // 在最后一个结点后加上新结点  
  22.             last.addNext(newNode);  
  23.         }  
  24.         size++;// 当前大小自增加1  
  25.         return true;  
  26.     }  
  27.   
  28.     public boolean insert(int index, E e) {  
  29.         Node<E> newNode = new Node<E>(e);  
  30.         // 得到第N个结点  
  31.         Node<E> cNode = getNode(index);  
  32.         newNode.next = cNode.next;  
  33.         cNode.next = newNode;  
  34.         size++;  
  35.         return true;  
  36.     }  
  37.   
  38.     /** 
  39.      * 遍历当前链表,取得当前索引对应的元素 
  40.      *  
  41.      * @return 
  42.      */  
  43.     private Node<E> getNode(int index) {  
  44.         // 先判断索引正确性  
  45.         if (index > size || index < 0) {  
  46.             throw new RuntimeException("索引值有错:" + index);  
  47.         }  
  48.         Node<E> tem = new Node<E>();  
  49.         tem = header;  
  50.         int count = 0;  
  51.         while (count != index) {  
  52.             tem = tem.next;  
  53.             count++;  
  54.         }  
  55.         return tem;  
  56.     }  
  57.   
  58.     /** 
  59.      * 根据索引,取得该索引下的数据 
  60.      *  
  61.      * @param index 
  62.      * @return 
  63.      */  
  64.     public E get(int index) {  
  65.         // 先判断索引正确性  
  66.         if (index >= size || index < 0) {  
  67.             throw new RuntimeException("索引值有错:" + index);  
  68.         }  
  69.         Node<E> tem = new Node<E>();  
  70.         tem = header;  
  71.         int count = 0;  
  72.         while (count != index) {  
  73.             tem = tem.next;  
  74.             count++;  
  75.         }  
  76.         E e = tem.e;  
  77.         return e;  
  78.     }  
  79.   
  80.     public int size() {  
  81.         return size;  
  82.     }  
  83.   
  84.     /** 
  85.      * 设置第N个结点的值 
  86.      *  
  87.      * @param x 
  88.      * @param e 
  89.      * @return 
  90.      */  
  91.     public boolean set(int index, E e) {  
  92.         // 先判断索引正确性  
  93.         if (index > size || index < 0) {  
  94.             throw new RuntimeException("索引值有错:" + index);  
  95.         }  
  96.         Node<E> newNode = new Node<E>(e);  
  97.         // 得到第x个结点  
  98.         Node<E> cNode = getNode(index);  
  99.         cNode.e = e;  
  100.         return true;  
  101.     }  
  102.   
  103.     /** 
  104.      * 用来存放数据的结点型内部类 
  105.      */  
  106.     class Node<e> {  
  107.         private E e;// 结点中存放的数据  
  108.         Node<E> next;// 用来指向该结点的下一个结点 
  109.         
  110. Node() { }  
  111.         Node(E e) {  
  112.             this.e = e;  
  113.         }  
  114.         // 在此结点后加一个结点  
  115.         void addNext(Node<E> node) {  
  116.             next = node;  
  117.         }  
  118.     }  
  119. }  

 

自己实现线性表之栈

         栈是限定仅允许在表的同一端(通常为“表尾”)进行插入或删除操作的线性表。

         允许插入和删除的一端称为栈顶(top),另一端称为栈底(base)
         特点:后进先出 (LIFO)或,先进后出(FILO)

 

         因为栈是限定线的线性表,所以,我们可以调用前面两种线性表,只需要对出栈和入栈操作进行设定即可

    具体实现代码

Java代码   收藏代码
  1. /** 
  2.  * 自己用数组实现的栈 
  3.  */  
  4. public class ArrayStack<E> {  
  5.       private ArrayList<E> list=new ArrayList<E>();//用来保存数据线性表<br>    private  int size;//表示当前栈元素个数  
  6.       /** 
  7.        * 入栈操作 
  8.        * @param e 
  9.        */  
  10.       public void push(E e){  
  11.           list.add(e);  
  12.           size++;  
  13.       }  
  14.        
  15.       /** 
  16.        * 出栈操作 
  17.        * @return 
  18.        */  
  19.       public E pop(){  
  20.          E e= list.get(size-1);  
  21.          size--;  
  22.          return e;  
  23.       }  
  24.   
  25. }  

 

 至于用链表实现栈,则只需要把保存数据的顺序表改成链表即可,此处就不给出代码了

 

自己实现线性表之队列

        与栈类似

        队列是只允许在表的一端进行插入,而在另一端删除元素的线性表。

        在队列中,允许插入的一端叫队尾(rear),允许删除的一端称为队头(front)。
        特点:先进先出 (FIFO)、后进后出 (LILO)

 

       同理,我们也可以调用前面两种线性表,只需要对队列的入队和出队方式进行处理即可

 

Java代码   收藏代码
  1. package cn.javamzd.collection.List;  
  2.   
  3. /** 
  4.  * 用数组实现的队列 
  5.  */  
  6. public class ArrayQueue<E> {  
  7.     private ArrayList<E> list = new ArrayList<E>();// 用来保存数据的队列  
  8.     private int size;// 表示当前栈元素个数  
  9.   
  10.     /** 
  11.      * 入队 
  12.      * @param e 
  13.      */  
  14.     public void EnQueue(E e) {  
  15.         list.add(e);  
  16.         size++;  
  17.     }  
  18.   
  19.     /** 
  20.      * 出队 
  21.      * @return 
  22.      */  
  23.     public E DeQueue() {  
  24.         if (size > 0) {  
  25.             E e = list.get(0);  
  26.             list.delete(0);  
  27.             return e;  
  28.         }else{  
  29.             throw new RuntimeException("已经到达队列顶部");  
  30.         }  
  31.     }  
  32. }  


对比线性表和链式表
         前面已经说过顺序表和链式表各自的特点,这里在重申一遍

         数组操作优点:1.通过指针快速定位到下标,查询快速

                     缺点:  1.数组声明时即需要确定数组大小。当操作中超过容量时,则需要重新声明数组,并且复制当前所有数据

                                  2.当需要在中间进行插入或者删除时,则需要移动大量元素(size-index个)    

 

         链表操作优点:1.因为每个结点记录下个结点的引用,则在进行插入和删除操作时,只需要改变对应下标下结点的引用即可

                       缺点:1.要得到某个下标的数据,不能通过下标直接得到,需要遍历整个链表。

 

         现在,我们通过进行增删改查操作来感受一次其效率的差异

         思路:通过两个表,各进行大数据量操作(3W)条数据的操作,记录操作前系统时间,操作后系统时间,得出操作时间

  实现代码如下

 

Java代码   收藏代码
  1. package cn.javamzd.collection.List;  
  2.   
  3. public class Test {  
  4.   
  5.     /** 
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.         //测试自己实现的ArrayList类和Linkedlist类添加30000个数据所需要的时间  
  10.         ArrayList<String> al = new ArrayList<String>();  
  11.         LinkedList<String> ll = new LinkedList<String>();  
  12.         Long aBeginTime=System.currentTimeMillis();//记录BeginTime  
  13.         for(int i=0;i<30000;i++){  
  14.             al.add("now"+i);  
  15.         }  
  16.         Long aEndTime=System.currentTimeMillis();//记录EndTime  
  17.         System.out.println("arrylist  add time--->"+(aEndTime-aBeginTime));  
  18.         Long lBeginTime=System.currentTimeMillis();//记录BeginTime  
  19.         for(int i=0;i<30000;i++){  
  20.             ll.add("now"+i);  
  21.         }  
  22.         Long lEndTime=System.currentTimeMillis();//记录EndTime  
  23.         System.out.println("linkedList add time---->"+(lEndTime-lBeginTime));  
  24.           
  25.         //测试JDK提供的ArrayList类和LinkedList类添加30000个数据所需要的世界  
  26.         java.util.ArrayList<String> sal=new java.util.ArrayList<String>();  
  27.         java.util.LinkedList<String> sll=new java.util.LinkedList<String>();  
  28.         Long saBeginTime=System.currentTimeMillis();//记录BeginTime  
  29.         for(int i=0;i<30000;i++){  
  30.             sal.add("now"+i);  
  31.         }  
  32.         Long saEndTime=System.currentTimeMillis();//记录EndTime  
  33.         System.out.println("JDK arrylist  add time--->"+(saEndTime-saBeginTime));  
  34.         Long slBeginTime=System.currentTimeMillis();//记录BeginTime  
  35.         for(int i=0;i<30000;i++){  
  36.             sll.add("now"+i);  
  37.         }  
  38.         Long slEndTime=System.currentTimeMillis();//记录EndTime  
  39.         System.out.println("JDK linkedList add time---->"+(slEndTime-slBeginTime));  
  40.     }  
  41.   
  42. }  

  得到测试结果如下: 

arrylist add time--->446
linkedList add time---->9767
JDK arrylist add time--->13
JDK linkedList add time---->12

 由以上数据,我们可知:

           1.JDK中的ArrayList何LinkedList在添加数据时的性能,其实几乎是没有差异的

           2.我们自己写的List的性能和JDK提供的List的性能还是存在巨大差异的

           3.我们使用链表添加操作,花费的时间是巨大的,比ArrayList都大几十倍

 

      第三条显然是跟我们最初的设计不相符的,按照我们最初的设想,链表的添加应该比顺序表更省时

      查看我们写的源码,可以发现:

      我们每次添加一个数据时,都需要遍历整个表,得到表尾,再在表尾添加,这是很不科学的

 

      现改进如下:设立一个Node<E>类的成员变量end来指示表尾,这样每次添加时,就不需要再重新遍历得到表尾

      改进后add()方法如下

Java代码  
  1. public boolean add(E e) {  
  2.     if (size == 0) {  
  3.         header.e = e;  
  4.     } else {  
  5.         // 根据需要添加的内容,封装为结点  
  6.         Node<E> newNode = new Node<E>(e);  
  7.         //在表尾添加元素  
  8.         last.addNext(newNode);  
  9.                                         //将表尾指向当前最后一个元素  
  10.         last = newNode;  
  11.     }  
  12.     size++;// 当前大小自增加1  
  13.     return true;  
  14. }  

       ArrayList添加的效率和JDK中对比起来也太低

       分析原因为:

       每次扩大容量时,扩大量太小,需要进行的复制操作太多

       现在改进如下:

       每次扩大,则扩大容量为当前的三倍,此改进仅需要更改ensureCapacity()方法中的一行代码,此处就不列出了。

 

改进后,再次运行添加元素测试代码,结果如下:

 

arrylist add time--->16
linkedList add time---->8
JDK arrylist add time--->7
JDK linkedList add time---->7

 虽然还有改进的空间,但是显然,我们的效果已经大幅度改进了,而且也比较接近JDK了


接下来测试插入操作的效率

  我们只需要将测试代码中的添加方法(add())改成插入方法(insert(int index,E e)),为了使插入次数尽可能多,我们把index都设置为0

测试结果如下:

 

arrylist inset time--->17
linkedList inset time---->13
JDK arrylist inset time--->503
JDK linkedList inset time---->11

多次测试,发现我们写的ArrayList在插入方法的效率都已经超过JDK了,而且也接近LinkedLst了。撒花!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构与算法 ---- 线性表 及Java实现 顺序表、链表、栈、队列 的相关文章

  • 数据结构中常见的树(BST二叉搜索树、AVL平衡二叉树、RBT红黑树、B-树、B+树、B*树)

    原文 http blog csdn net sup heaven article details 39313731 数据结构中常见的树 BST二叉搜索树 AVL平衡二叉树 RBT红黑树 B 树 B 树 B 树 转载 2014年09月16日
  • 《Linux From Scratch》第三部分:构建LFS系统 第六章:安装基本的系统软件- 6.29. Coreutils-8.23...

    Coreutils 软件包包含用于显示和设置基本系统特性的工具 大概编译时间 2 5 SBU 需要磁盘空间 193 MB 6 29 1 安装 Coreutils POSIX 要求 Coreutils 中的程序即使在多字节语言环境也能正确识别
  • 白盒测试相关的一些知识

    在白盒测试中 可以使用各种测试方法进行测试 下面这篇文章 可能比较枯燥 如果不乐意读 可以先收藏 如果在你的工作中真遇到白盒测试的话 可以回过头再来看看 还是值得读一读 一般来说 白盒测试时要考虑以下5个问题 1 测试中尽量先用自动化工具来
  • PCL—低层次视觉—点云分割(RanSaC)

    点云分割 点云分割可谓点云处理的精髓 也是三维图像相对二维图像最大优势的体现 不过多插一句 自Niloy J Mitra教授的Global contrast based salient region detection出现 最优分割到底鹿死
  • OpenJDK源码阅读-Oop&Klass

    文章目录 oop klass OBJECT hierarchy metadata hierarchy klass hierarchy oop classDiagram description klass classDiagram descr
  • 手把手教你实现一个向量

    文章目录 什么是向量 向量提供哪些接口 实现 宏定义 定义类 成员变量 构造函数与析构函数 构造函数 析构函数 成员函数 size get r put r e expand insert r e remove lo hi remove r
  • Python 实现列队

    1 列队定义 队列是项的有序结合 其中添加新项的一端称为队尾 移除项的一端称为队首 当一个元素从队尾进入队列时 一直向队首移动 直到它成为下一个需要移除的元素为止 最近添加的元素必须在队尾等待 集合中存活时间最长的元素在队首 这种排序成为
  • 『Python基础-15』递归函数 Recursion Function

    什么是递归函数 一种计算过程 如果其中每一步都要用到前一步或前几步的结果 称为递归的 用递归过程定义的函数 称为递归函数 例如连加 连乘及阶乘等 凡是递归的函数 都是可计算的 即能行的 递归就是一个函数在它的函数体内调用它自身 编程语言中的
  • 图 - Java实现无向带权图的邻接矩阵表示法

    图 Java实现无向带权图的邻接矩阵表示法 1 图 1 1 图的介绍 图 Graph 是一种复杂的非线性表结构 图中的元素我们就叫做顶点 vertex 图中的一个顶点可以与任意其他顶点建立连接关系 我们把这种建立的关系叫做边 edge 跟顶
  • Unique Binary Search Trees -- LeetCode

    原题链接 http oj leetcode com problems unique binary search trees 这道题要求可行的二叉查找树的数量 其实二叉查找树可以任意取根 只要满足中序遍历有序的要求就可以 从处理子问题的角度来
  • 如何防止过拟合和欠拟合

    过拟合和欠拟合是模型训练过程中经常出现的问题 两种情况正好相反 现将两者的定义及如何防止进行简要总结 1 过拟合 1 1 定义 是指模型对于训练数据拟合呈现过当的情况 反映到评估指标上就是模型在训练集上的表现很好 但是在测试集上的表现较差
  • 用指针访问一维数组

    文章目录 顺序查找 数组方式实现 指针实现方式 对一位数组元素的访问有三种方式 指针变量的关系运算 引例 数组实现方式 主函数 指针实现方式 主函数 一维数组作为函数的参数 实际应用 顺序查找 要求用指针实现 在整数集合r中顺序查找与给定值
  • 二叉树结构的建立与遍历

    实验项目 1 编写建立二叉树的二叉链表存储结构 左右链表示 的程序 并以适当的形式显示和保存二叉树 2 完成二叉树的7种遍历操作 3 给定一个二叉树 编写算法完成下列应用 1 判断其是否为完全二叉树 2 求二叉树中任意两个结点的公共祖先 输
  • 查找数组中第二大的数

    快速找出一个数组中的最大数 第二大数 思路 如果当 前元素大于最大数 max 则让第二大数等于原来的最大数 max 再把当前元素的值赋给 max 如果当前的元素大于等于第二大数secondMax的值而小于最大数max的值 则要把当前元素的值
  • 插入排序超详解释,一看就懂

    目录 一 插入排序的相关概念 1 基本思想 2 基本操作 有序插入 二 插入排序的种类 三 直接插入排序 1 直接插入排序的过程 顺序查找法查找插入位置 2 使用 哨兵 直接插入排序 四 直接插入排序算法描述 五 折半插入排序 1 查找插入
  • 雪糕的最大数量 排序+贪心

    雪糕的最大数量 雪糕的最大数量 题目描述 样例 数据范围 思路 代码 题目描述 夏日炎炎 小男孩 Tony 想买一些雪糕消消暑 商店中新到 n 支雪糕 用长度为 n 的数组 costs 表示雪糕的定价 其中 costs i 表示第 i 支雪
  • Leetcode2661. 找出叠涂元素

    Every day a Leetcode 题目来源 2661 找出叠涂元素 解法1 哈希 题目很绕 理解题意后就很简单 由于矩阵 mat 中每一个元素都不同 并且都在数组 arr 中 所以首先我们用一个哈希表 hash 来存储 mat 中每
  • 按照层次遍历结果打印完全二叉树

    按照层次遍历结果打印完全二叉树 按照推论结果 l 层首个节点位置 2 h l 1 l 层节点间距 2 h l 1 1 编码实现 public static
  • 从源码角度来谈谈 HashMap

    HashMap的知识点可以说在面试中经常被问到 是Java中比较常见的一种数据结构 所以这一篇就通过源码来深入理解下HashMap 1 HashMap的底层是如何实现的 基于JDK8 1 1 HashMap的类结构和成员 HashMap继承
  • 最大流-Dinic算法,原理详解,四大优化,详细代码

    文章目录 零 前言 一 概念回顾 可略过 1 1流网络 1 2流 1 3最大流 1 4残留网络 1 5增广路

随机推荐

  • 虹膜识别系统 python实现

    先上传效果图 main py An highlighted block Demonstration of the GazeTracking library Check the README md for complete documenta
  • 【从嵌入式视角学习香山处理器】六、NutShell代码结构(乱画的框图)

    文章目录 一 前言 二 简单粗暴版 最终成品的框图 三 不要太凌乱版 去掉连线后的框图 一 前言 这是从上一篇文章 从嵌入式视角学习香山处理器 五 香山开发工作流实践1 主要子模块工程之间的关系 引出的对果壳核 NutShell 一个简单入
  • MySQL存储过程入门教程及实例详解

    1 存储过程简介 存储过程是可编程的函数 在数据库中创建并保存 可以由SQL语句和控制结构组成 当想要在不同的应用程序或平台上执行相同的函数 或者封装特定功能时 存储过程是非常有用的 数据库中的存储过程可以看做是对编程中面向对象方法的模拟
  • 不同集合中判断元素相同的方法

    判断集合中的元素是否相同 对于增删改查有重要意义 不同Collection的实现的判断依据不同 1 List类 线性表 统一标准是equals 2 HashSet和HashMap 哈希表 先hashcode 后equals 3 TreeSe
  • k8s selector_k8s(二)——kubectl的使用以及创建deployment

    kubectl的使用以及创建deployment kubectl的使用 常见概念 kubectl管理命令概要 管理和使用deployment 基于deployment创建nginx pod 有一个副本 查看k8s对象状态 发布应用 服务伸缩
  • SpringBoot:切面AOP实现权限校验:实例演示与注解全解

    1 理解AOP 1 1 什么是AOP AOP Aspect Oriented Programming 面向切面思想 是Spring的三大核心思想之一 两外两个 IOC 控制反转 DI 依赖注入 那么AOP为何那么重要呢 在我们的程序中 经常
  • SM4 CBC模式加密的C语言实现

    因为工作的关系 最近在研究国密算法 其中无线局域网使用的SM4算法颇为神秘 网上资源也是少的可怜 不过在笔者的努力下 还是成功搞定了 有感于SM4相关正确资料的稀少 同时也算是自我的学习积累 故写下此文 希望可以帮助后来人少走些弯路 此处给
  • 有意思的心理学现象

  • Elasticsearch 实战之三:ES 基本操作

    目录 0 数据格式说明 1 ES的基本操作 1 1 索引操作 1 1 1 建立索引 1 1 2 删除索引 1 1 3 查询索引 1 2 映射操作 1 2 1 建立映射 1 2 2 查询映射 1 3 基本操作 CRUD 1 3 1 新增和替换
  • Zephyr-环境搭建

    目录 1 前言 2 安装主机依赖 3 获取源码 4 安装工具链 5 编译一个Demo 1 前言 Zephyr支持Ubuntu macOS Windows环境下开发 本文介绍基于Ubuntu的环境搭建 包括 Ubuntu开发环境搭建 主要是工
  • VBA设置默认/缺省运行路径的方法

    在设计VBA脚本时 有时我们需要在指定目录中运行或打开第三方程序 文件 那么最好的方法就是使用ChDir命令更改当前默认的运行路径 以便脚本能轻松地找到所需文件 更改缺省文件夹或驱动器 ChDir 语句和ChDrive语句 使用ChDir语
  • 改进的PSO-BP算法在工业机器人末端位姿误差补偿中的应用

    摘要 提出了一种全梯度标准粒子群优化反馈 FGSPSO BP 神经网络的工业机器人末端位姿补偿模型 首先 提出一种运动学逆变换算法 通过机器人末端位姿对机器人各关节角度值进行计算 并采用Matlab验证了运动学逆变换算法的准确性 然后 提出
  • VS2019打包WPF安装程序最新教程

    VS2019打包WPF安装程序最新教程 使用Visual Studio 2019开发的WPF程序如果想要打包为安装程序 除了在VS2019找到WPF项目类库直接右键发布之外 更常用的还是将其打包为exe或者msi的安装程序 打包成安装程序的
  • 2022电赛小车开源代码讲解开源

    2022电赛小车我认为有主要是几个主要的问题 我将分这几个部分来讲解 目录 一 循迹 二 蓝牙通信 双车数据传输 三 起始路口的识别 四 分叉路口的识别 五 源码 2022电赛 双车稳定行驶 哔哩哔哩 bilibili 一 循迹 循迹我们组
  • 自定义yaml文件调整pandoc和pandoc-crossref将markdown转word过程中公式行距

    markdown借助pandoc和pandoc crossref将写好的学术论文导出成word时 公式为OMML格式 这个格式可以通过word中的mathtype插件批量转换 但是公式的基准为正文 如果正文文本的段落格式为固定值20磅 比较
  • 使用Java生成全部数独(Sudoku)布局

    b 引言 b 数独相信很多人都玩过 趣味性很强 十分的耐玩 可有没有程序员想过玩实现一个数独布局的算法呢 算法是个很有意思 很神奇的东西 我第一次接触算法是刚学C的时候 写DOS下的挖雷程序 当时还是WIN32 C的编译器还是TC3 0 当
  • Python基础篇(十一):装饰器

    装饰器 前言 1 装饰器的定义 2 装饰器的应用 3 装饰器的语法 4 func args kwargs 前言 装饰器是Python中一种强大的函数或类修饰机制 用于在不修改原始函数或类代码的情况下 对其进行功能扩展或修改 装饰器基于函数式
  • python基础学完还要学什么_Python学完基础语法后,再往后应该学什么?

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 第一阶段 Python语言及应用 课程内容 Python语言基础 面向对象设计 多线程编程 数据库交互技术 前端特效 Web框架 爬虫框架 网络编程 掌握技能 1 掌握Python语言语法及面向
  • 教程资源(持续更新中)

    https www liwenzhou com posts Go golang menu 下面这个也很牛逼 the way to go ZH CN directory md at master piexlmax the way to go
  • 数据结构与算法 ---- 线性表 及Java实现 顺序表、链表、栈、队列

    数据结构与算法是程序设计的两大基础 大型的IT企业面试时也会出数据结构和算法的题目 它可以说明你是否有良好的逻辑思维 如果你具备良好的逻辑思维 即使技术存在某些缺陷 面试公司也会认为你很有培养价值 至少在一段时间之后 技术可以很快得到提高