zoukankan      html  css  js  c++  java
  • 数据结构(1)

    数据结构

    数据结构其实就是一种存储数据的格式。可以有效的改善代码中数据的存储。


    稀疏矩阵

    对于一个二维数组,如果数组中大部分元素为0,那么会造成内存空间极大的浪费。因此,设计一种针对稀疏数组的数据结构就很有必要,例如:

     

     可以看出,稀疏矩阵是将一种矩阵转换,将N行M列的矩阵转换为X行3列的矩阵,当矩阵为稀疏矩阵时,这种存储数据的结构更能符合压缩的功能。

    二维数组转换为稀疏矩阵

    步骤1:保存稀疏矩阵的第一行,N、M、count(遍历二维数组得到非0元素个数)

    步骤2:遍历数组,保存非0数组的行数、列数、以及元素的值。

    稀疏矩阵转换为二维数组

    步骤1:从第一行获得二维数组的行、列。

    步骤2:遍历稀疏矩阵,将二维数组中对应位置填值。


    •  

    队列

    队列是一种先进先出的数据结构,队列在计算机系统中有很重要的作用,比如在缓存机制中,就是将磁盘的值按照队列形式排列,先存进来的优先被cpu使用。

    (一种顺序队列) 

    队列的元素:长度、队头、队尾。

    顺序队列

    顺序队列在物理中的存储方式是连续的,可以保存在数组中。

    初始化:队列需要使用一个队头和队尾的指示标志(指针/索引),队头和队尾一开始一定是相等的(队头队尾相等是空队的判断条件)。

    判空:队头=队尾

    判满:在非循环队列中,队满的标志仅是:队尾==MaxSize-1(指向最后一个元素);在循环队列中,队满的标志是:(队尾+1) mod MaxSize==队头(即在循环上,队尾比队头多一个元素)

    入队:首先,判断队列是否已满,满了就无法入队,若没有满,则将元素赋值给队尾,并使队尾指向下一个索引(这里的下一个是相对循环而言,若下一个索引大于MaxSize-1,则需指向第一个元素),需取模:     

    rear=(rear+1)%MaxSize

    出队:首先,判断队列是否为空,若是空队就无法取出元素,若非空队,则从队头取元素,即,将队头指向的元素取出并返回,然后使队头的元素指向下一个索引。(同样的,下一个需要判断其索引是否大于长度),需取模。

    front=(front+1) % MaxSize

    显示队列里的值:用这种方法得到的队列,其数组并不是真的入队出队,数据虽然被覆盖,但并没有删除,因此,出队操作并没有出数组。因此,显示的时候需要判断,队头和队尾,然后将有效的数据显示出来。这里就有一个有效数据的概念,这里的有效数据是:

    (rear-front +MaxSize) % MaxSize

    因此,遍历的起点是队头,队尾是队头+有效数据。而索引值则需要取模处理,因为可能超过长度。


    •  

    单链表

    链表是树和图的基础,也是区别于顺序存储的另一种存储方式。在jvm中,如果一个对象不被引用,则会被垃圾回收,这里就用到了链表的实现。

    最简单的链表就是单链表,单链表的增删改查是很简单的。这里不多介绍。主要举几个面试的例子。

    1.单链表中有效节点的个数

    很简单,遍历链表,当当前节点的next域不为空时,就计数。

    2.查找单链表中倒数第k个节点

    方案1:先得到链表的长度,然后根据计算查找第(长度-k)个节点,即可得到该节点。

    方案2:使用两个辅助指针,第一个指向第k个节点,第二个指向第1个节点,然后两个指针同时向后移动,当指向第一个的节点移到最后,则第二个指针指向的就是倒数第k个。

    3.单链表的翻转

    使用头插法,新建一个链表,每次遍历得到节点就插到新链表的头部,这样先插的会到后面去,实现了链表的翻转。

    4.从尾到头打印单链表

    使用一个辅助栈,先将遍历得到的节点值依次压栈,然后遍历结束之后,依次出栈。

    5.合并两个有序的单链表,合并后仍然有序

    定义一个新节点,然后定义两个游标指向两个链表的头结点,比较大小,如果小就将该节点插入新节点的后面,循环进行,直到其中一个链表被遍历完,然后将剩下的链表插入到新节点的后面。



    双向链表

    设计链表的时候,有前指针域和后指针域。双向链表不需要翻转。

    删除

    对于单链表而言,要想删除当前节点,不能直接遍历到当前的节点,而是需要遍历到当前节点的前一个,然后将其next域赋值给当前节点的next域。

    双向链表不需要这么做,遍历到当前节点,将当前节点的next域赋值给当前节点的pre域,将后面节点的pre域赋值给当前节点的next域。然后删除即可。

    单向环链表

    该链表只比单链表增加了一条链表末尾指向链表开头的指针。

    解决约瑟夫环问题

     稍加分析就可以知道,设置两个指针,first和help,初始化时,first指向k,help指向k-1,表示的是first和help一个在头,一个在尾,然后后移m个,将help指向first后一个节点,形成环路,然后删除first指向的节点,将first+1,依次循环直到环为空。


    •  

    栈是一种先进后出的数据结构,在计算机操作系统里面有很广泛的应用。

     使用栈来完成一个计算器。

    1.中缀表达式

    中缀表达式就是计算表达式的原始表达式,例如

    2*(3+2)-6/3

    直接利用中缀表达式,配合栈的使用,可以得到计算结果。

     这里主要是扫描到符号的时候,需要判断符号的优先级。如果栈中的优先级大于当前的,就需要先计算,然后再压栈。其实这里的算法是有问题的

    3+4-8/2+5*5/5-8+9

    像上面的计算式,如果采用上面的流程,会导致最后7-4+5-8+9出现,计算从后往前,会破坏四则运算从左往右的计算规则:结果为7,实际结果为9改进在3.2,代码如下:

    public int cal(String str){
    
            Stack<String> num=new Stack<String>();
            Stack<String> oper=new Stack<String>();
    
            int index=0;
            int sum=0;
            while(true){
                //如果该字符是一个符号
                if(isoper(str.charAt(index))){
                    if(oper.isEmpty()){
                        oper.push(str.substring(index,index+1));
                    }else {
                        if(priority(str.charAt(index))<=priority(oper.peek().charAt(0))){
                            //先pop出来,然后计算
                            while(!num.isEmpty() && !oper.isEmpty() && priority(str.charAt(index))<=priority(oper.peek().charAt(0))) {
                                int num1 = Integer.parseInt(num.pop());
                                int num2 = Integer.parseInt(num.pop());
                                char ope = oper.pop().charAt(0);
                                int res = call(num1, num2, ope);
                                num.push(("" + res));
                            }
                            oper.push(str.substring(index, index + 1));
                        }else{
                            oper.push(str.substring(index,index+1));
                        }
                    }
                }else{
                    num.push(str.substring(index,index+1));
                }
                index++;
                if(index>=str.length()){
                    break;
                }
            }
    View Code

    2.后缀表达式

    将中缀表达式转换为后缀表达式之后,就不需要判断优先级,直接进行运算,得到的就是结果

    如何将中缀表达式转换为后缀表达式

     如何用中缀表达式计算



    递归

    递归是函数自己调用自己,但事实上,它在调用过程中和普通调用没有区别。

    递归调用的机制。

    从上图可以看出,执行main方法后在栈空间开辟一个栈的数据结构,mian方法在栈底,调用test方法,一旦方法进行了调用,就会在栈空间入栈,test方法里有调用,仍然会入栈,。。。直到,不满足调用条件,会执行调用之后的打印代码,然后当调用函数执行完,就会自动出栈操作,释放空间。然后返回上一层的打印代码,。。。。直到main函数执行结束。

    递归执行的规则



    回溯

    在迷宫问题上,如何从入口找到出口。可以使用回溯,回溯问题可以使用递归来解决,即设置多个递归入口,设置迷宫的寻找策略。

    分析:从入口开始,尝试从上下左右四个方向去寻找出口,如果四个方向都尝试了以后还没有找到出口,说明这个路是不通的,需要往回走,这就是回溯。

     

    代码如下,可以分析其递归调用的过程。

    main方法入栈,

    调用setWay方法,入栈,map为引用类型,不会在调用中终止寿命,因此会发生改变。但基础类型会发生改变。此时是从(1,1)开始,将其设置为2,然后调用setWay(map,0,1)。

    调用后直接返回false,回溯到栈(1,1),

    调用setWay(map,1,2),可以进入,将其设置为2,然后直接调用setWay(map,0,2),直接返回false,回溯到栈(1,1)。。。。

    。。。

    八皇后问题,通过回溯递归来解决

     限制:每一行必有且只有一个皇后。

    需要确定的摆放位置:第一行的皇后放在第几列,第二行的皇后放在第几列。。。。。可以使用一个数组:int[] array = new int[8];,下标代表的行,值代表的列。

    private void check(int n) {
            if(n == max) {  //n = 8 , 其实8个皇后就既然放好
                print();
                return;
            }
            
            //依次放入皇后,并判断是否冲突
            for(int i = 0; i < max; i++) {
                //先把当前这个皇后 n , 放到该行的第1列
                array[n] = i;
                //判断当放置第n个皇后到i列时,是否冲突
                if(judge(n)) { // 不冲突
                    //接着放n+1个皇后,即开始递归
                    check(n+1); //  
                }
                //如果冲突,就继续执行 array[n] = i; 即将第n个皇后,放置在本行得 后移的一个位置
            }
        }

    对于以上递归代码,每次循环都会有八个递归入口,判断是否进入的依据是该入口是否满足八皇后的要求。



    冒泡排序

    冒泡排序的精髓在于每一次循环都会把数组的最后一个值确定下来,或是最大,或是最小。

    从以上的例子就可以看出。每次循环都能找到比较里面最大的,然后把它交换到最后,下一次循环就不会比较这个值。时间复杂度为T(n^2)

    package com.liuixnghang.test;
    
    import org.junit.Test;
    
    public class Maopao {
    
    
        @Test
        public void run(){
    
            int [] a=new int[]{2,1,3,5,4};
            maopao(a);
    
        }
    
        public void print(int[] a){
    
            for (int i : a) {
                System.out.print(i +"   ");
            }
            System.out.println();
        }
    
    
        public void maopao(int[] a){
    
            boolean falg=false;
            for(int i=0;i<a.length-1;i++){
    
                for(int j=0;j<a.length-1-i;j++){
    
                    if(a[j]>a[j+1]){
                        int temp=a[j];
                        a[j]=a[j+1];
                        a[j+1]=temp;
                        falg=true;
                    }
    
                }
                if (falg==false){
                    break;
                }else{
                    falg=false;
                }
                print(a);
    
            }
        }
    }
    View Code

    选择排序

    选择排序重在选择,为了避免一直交换,可以设置一个min值,每次都遍历一遍数组,得到最小值,将其与数组第一个值交换,这样,循环长度-1次后,就得到了排序。

     可以知道这样避免了一直去交换

    代码如下:

    package com.liuixnghang.test;
    
    import org.junit.Test;
    
    public class Choose {
    
    
        @Test
        public void run(){
            int [] a=new int[]{101,34,199,1,-1,90,123};
            choose(a);
    
        }
    
    
        public void choose(int[] a){
    
            for(int i=1;i<a.length;i++){
                int minIndex=i-1;
                int min=a[minIndex];
                //每次循环都会选择最小的数出来放在第一个地方
                for(int j=i;j<a.length;j++){
                    if(a[j]<min){
                        min=a[j];
                        minIndex=j;
                    }
                }
                if(minIndex!=(i-1)){
                    a[minIndex]=a[i-1];
                    a[i-1]=min;
                }
                print(a);
    
            }
        }
        public void print(int[] a){
    
            for (int i : a) {
                System.out.print(i +"   ");
            }
            System.out.println();
        }
    
    
    }
    View Code

    插入排序

    其主要思想是,将一部分先排好序,每次从后面的一个往有序的里面插,形成新的有序序列,直到所有都有序。

     插入过程是先从有序的最后一个开始找自己的插入位置。

    代码如下:

    package com.liuixnghang.test;
    
    import org.junit.Test;
    
    public class Insert {
    
        @Test
        public void run(){
    
            int [] a=new int[]{4,7,2,32090,-1218,-9,238,1219,-11,2};
            insert(a);
    
        }
    
        public void print(int[] a){
    
            for (int i : a) {
                System.out.print(i +"   ");
            }
            System.out.println();
        }
    
        public void insert(int[] a){
    
            for(int i=1;i<a.length;i++){
    
                //待插入的值
                int insertValue=a[i];
                //插入位置
                int insertIndex=i-1;
    
                //从第i-1个位置开始寻找插入位置,没找到需要将数组向后移
                while(insertIndex>=0 && insertValue<a[insertIndex]){
                    a[insertIndex+1]=a[insertIndex];
                    insertIndex--;
                }
    
                a[insertIndex+1]=insertValue;
                print(a);
            }
    
        }
    }
    View Code

    希尔排序

    是插入排序的升级版,在插入排序中,如果较小的元素在最后面,那么插入效率很变得很慢,为了解决这个问题,可以把数据隔着步长分组,对每组的数据进行插入排序,这样就可以快速的使小的数据移动到前面来。

     在刚开始分为5组,插入排序的对象是两个数据,然后变成五个,最后变为10个。步长在不断的改变当中。

    使用交换法实现。不是使用移动来插入,而是每次都插入实现插入。

    package com.liuixnghang.test;
    
    import org.junit.Test;
    
    public class Shell {
    
        @Test
        public void run(){
    
            int [] a=new int[]{8,9,1,7,2,3,5,4,6,0};
            shell(a);
    
        }
    
        public void print(int[] a){
    
            for (int i : a) {
                System.out.print(i +"   ");
            }
            System.out.println();
        }
    
        public void shell(int[] a){
    
    
            for(int gap=a.length/2;gap>0;gap/=2) {
    
                for (int i = gap; i < a.length; i++) {
    
    
                    for (int j = i - gap; j >= 0; j -= gap) {
    
                        if (a[j] > a[j + gap]) {
                            int temp = a[j];
                            a[j] = a[j + gap];
                            a[j + gap] = temp;
                            print(a);
                        }
                        System.out.println("----");
    
                    }
                    System.out.println("----");
                }
                System.out.println("----");
            }
    
    
        }
    }
    View Code

    使用移动法插入,就是对每次分组采用插入排序。

    package com.liuixnghang.test;
    
    import org.junit.Test;
    
    public class Shell {
    
        @Test
        public void run(){
    
            int [] a=new int[]{8,9,1,7,2,3,5,4,6,0};
            shell2(a);
    
        }
    
        public void print(int[] a){
    
            for (int i : a) {
                System.out.print(i +"   ");
            }
            System.out.println();
        }
    
        public void shell(int[] a){
    
    
            for(int gap=a.length/2;gap>0;gap/=2) {
    
                for (int i = gap; i < a.length; i++) {
    
    
                    for (int j = i - gap; j >= 0; j -= gap) {
    
                        if (a[j] > a[j + gap]) {
                            int temp = a[j];
                            a[j] = a[j + gap];
                            a[j + gap] = temp;
                            print(a);
                        }
    
                    }
                    print(a);
                }
            }
        }
    
        public void shell2(int[] a){
            //使用移动法
            for(int gap=a.length/2;gap>0;gap/=2){
                for(int i=gap;i<a.length;i++){
    
                    //保存待移动的数
                    int temp=a[i];
                    int j=i-gap;
    
                    while(j>=0 && temp<a[j]){
                        a[j+gap]=a[j];
                        j-=gap;
                    }
    
                    a[j+gap]=temp;
                    print(a);
                }
            }
    
    
        }
    
    }
    View Code

    快速排序

    快速排序是冒泡排序改进而来,通过空间换时间,有效的降低了时间复杂度,冒泡排序每次固定一个最大值排在后面,快速排序每次固定一个基准数放在其位置上。

     快速排序也有两种实现方式,主要区别在于如何移动数据,使得基准数据排在准确位置。

    1.挖坑法:

    设置两个指针left和right指向数组的头和尾,选取基准数,可随机选择。但需要将该基准数移动第一位。

    记住选取基准位置的index,此索引就是坑。

    从尾部right开始,向前寻找比基准数小的数,找不到就一直向前移,直到找到这样的数(前提是right要比left大)

    将找到的这个数填入坑中,此时,right指向的数成为了新的坑

    然后left从头部开始,向后寻找比基准大的数,找不到就一直向后,直到找到,(前提是right要比left大)

    找到这个数,继续填坑,此时left指向的数成为了新坑。

    直到right和left重合。这一轮就结束了,

    把基准的数填在重合的索引里,然后

    然后进行递归,在right左边,继续进行这样的操作。

    代码如下:

    package com.liuixnghang.test;
    
    import org.junit.Test;
    
    public class Waken {
    
    
        @Test
        public void run(){
    
            int [] a=new int[]{8,9,1,7,2,3,5,4,6,0};
            waken(a,0,a.length-1);
    
        }
    
        public void print(int[] a){
    
            for (int i : a) {
                System.out.print(i +"   ");
            }
            System.out.println();
        }
    
        public void waken(int a[],int left,int right){
    
            //设置索引
            int l=left;
            int r=right;
    
            //设置基准
    
            int juzhun=a[left];
    
            //循环填坑
            if(l<r) {
                while (l != r) {
    
                    //从尾部开始,找比基准小的元素
                    while (l < r && a[r] >= juzhun) {
                        r--;
                    }
                    //找到了这样的数,将值填到坑中
                    if (l < r) {
                        a[l] = a[r];
                        l++;
                    }
    
                    //从尾部开始,找比基准小的元素
                    while (l < r && a[l] <= juzhun) {
                        l++;
                    }
                    //找到了这样的数,将值填到坑中
                    if (l < r) {
                        a[r] = a[l];
                        r--;
                    }
    
                }
                a[l] = juzhun;
                print(a);
                waken(a, left, r - 1);
                waken(a, l + 1, right);
    
            }
        }
    }
    View Code

    2.指针交换法

    设置两个指针left和right指向数组的头和尾,选取基准数,可随机选择。但需要将该基准数移动第一位。

    从left指针和right指针开始,向中间靠拢,如果left遇到比基准大的指针,需要停下来,right遇到比基准小的指针,也需要停下来。

    两个指针指向的数交换。然后继续交换,直到两个指针重合。然后将第一位中基准元素与重合位元素交换,此轮结束。

    代码如下:

    package com.liuixnghang.test;
    
    import org.junit.Test;
    
    public class Jiaohuan {
        @Test
        public void run(){
    
            int [] a=new int[]{8,9,1,7,2,3,5,4,6,0};
            jiaohuan(a,0,a.length-1);
    
        }
    
        public void print(int[] a){
    
            for (int i : a) {
                System.out.print(i +"   ");
            }
            System.out.println();
        }
    
        public void jiaohuan(int[] a, int left, int right){
    
            //设置索引
            int l=left;
            int r=right;
    
            //设置基准
    
            int juzhun=a[left];
            while(l!=r){
    
                //寻找left指向比基准小的数
                while(l<r &&a[r]>juzhun){
                    r--;
                }
    
                //寻找left指向比基准大的数
                while(l<r &&a[l]<=juzhun){
                    l++;
                }
    
    
    
                if(l<r){
                    int temp=a[l];
                    a[l]=a[r];
                    a[r]=temp;
                }
    
            }
            //需要退指针
    
            //将基准数和相等时的位置交换
    
            int temp=a[l];
    
            a[l]=a[left];
            a[left]=temp;
            print(a);
    
            jiaohuan(a,left,r-1);
            jiaohuan(a,l+1,right);
        }
    }
    View Code

    (需要注意的一点是,需要先移动r指针,才会在重合的位置是所需要的)

    归并排序

    归并排序使用了分治的思想,先利用递归,将数组分为单个的数,然后在递归出栈的时候,就可以每两个进行排序,然后到下一个栈,每四个进行排序,到最后合并为一个有序数组

     归并排序说明:其递归可以这样设计:第一次分为一半,然后分为四分之一。。。。直到分为1,无法再分。

    按照递归的栈原理,需要返回,在返回之前,将之前分开的按左右合并,合并规则就是小的在前,大的在后。

    每次返回之前都合并,到返回mian函数的时候已经合并为一个有序的数组了。

    代码如下:

    package com.liuixnghang.test;
    
    import org.junit.Test;
    
    public class GuiBing {
    
        @Test
        public void run(){
    
            int [] a=new int[]{8,9,1,7,2,3,5,4,6,0};
            int [] temp=new int[a.length];
            guibing(a,0,a.length-1,temp);
    
    
            print(a);
    
        }
    
        public void print(int[] a){
    
            for (int i : a) {
                System.out.print(i +"   ");
            }
            System.out.println();
        }
    
        public void guibing(int [] a,int left,int right,int[] temp){
    
            if(left<right){
                int mid=(left+right)/2;
                //向左递归进行分解
                guibing(a,left,mid,temp);
                guibing(a,mid+1,right,temp);
                hebing(a,left,mid,right,temp);
            }
    
    
        }
    
        public void hebing(int[] a,int left,int mid,int right,int[] temp){
    
            //将左边和右边排序
            int i=left;//左边数组的起始索引
            int j=mid+1;//右边数组的起始索引
    
            int t=0;//t数组的起始索引
    
            while(i<=mid && j<=right){
    
                if(a[i]<=a[j]){
                    temp[t]=a[i];
                    t++;
                    i++;
                }else{
                    temp[t]=a[j];
                    t++;
                    j++;
                }
            }
    
            while(i<=mid){
                temp[t]=a[i];
                t++;
                i++;
            }
    
            while(j<=right){
                temp[t]=a[j];
                t++;
                j++;
            }
    
            //将temp中的数组拷贝在a中
            t=0;
            int tempLeft=left;
            while(tempLeft<=right){
                a[tempLeft]=temp[t];
                t++;
                tempLeft++;
            }
    
        }
    }
    View Code

    基数排序

    经典的用空间换时间的排序方式,排序需要10个数组,每个数组的长度与待排序数组长度一样多,很耗费空间。

     以上就是基数排序的过程:

    设置10个数组,逻辑上代表位数的个数位。

    在第一轮排序中,按照数组中每个数的个位,将数依次填入每个桶中;

    然后根据存的桶再依次拿出存入数组中,依次往复,

    即可得到排序后的数组。

    代码如下:

    package com.liuixnghang.test;
    
    import org.junit.Test;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class Jishu {
    
        @Test
        public void run(){
    
            //测试快排的执行速度
            // 创建要给80000个的随机的数组
            int[] arr = new int[8000000];
            for (int i = 0; i < 8000000; i++) {
                arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数
            }
    
            System.out.println("排序前");
            Date data1 = new Date();
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String date1Str = simpleDateFormat.format(data1);
            System.out.println("排序前的时间是=" + date1Str);
    
            jishu(arr);
    
            Date data2 = new Date();
            String date2Str = simpleDateFormat.format(data2);
            System.out.println("排序前的时间是=" + date2Str);
    
        }
    
        public void print(int[] a){
    
            for (int i : a) {
                System.out.print(i +"   ");
            }
            System.out.println();
        }
    
        public void jishu(int [] a){
    
            //设置1个二维数组来存储10个一维数组
            int [][] bucket=new int[10][a.length];
    
            //设置一个数组来记录每个桶中的数据有几个
            int bucketElementCounts[]=new int[10];
    
            //这里需要得到数据中数的最大位数
            int max=0;
            for(int i=0;i<a.length;i++){
                if(a[i]>max){
                    max=a[i];
                }
            }
            int maxLength=(max+"").length();
    
            //进行循环
            for(int i=0,n=1;i<maxLength;i++,n*=10){//对每一位进行处理
                //把数据取出来放到对应的桶里
                for(int j=0;j<a.length;j++){
                    //得到当前数据的位数
                    int digit=a[j] /n %10;
                    //放到对应的桶中
                    bucket[digit][bucketElementCounts[digit]]=a[j];
                    bucketElementCounts[digit]++;
                }
                int index=0;
                //按桶的顺序,取出数,放到数组中
    
                for(int k=0;k<bucketElementCounts.length;k++){
                    if(bucketElementCounts[k]!=0){
                        for(int l=0;l<bucketElementCounts[k];l++){
                            a[index++]=bucket[k][l];
                        }
                    }
                    //将bucketElementCounts中的数清零
                    bucketElementCounts[k]=0;
                }
    
            }
    
    
        }
    }
    View Code

    排序总结

  • 相关阅读:
    Study Plan The TwentySecond Day
    Study Plan The Nineteenth Day
    Study Plan The TwentySeventh Day
    Study Plan The Twentieth Day
    Study Plan The TwentyFirst Day
    python实现进程的三种方式及其区别
    yum makecache
    JSONPath 表达式的使用
    oracle执行cmd的实现方法
    php daodb插入、更新与删除数据
  • 原文地址:https://www.cnblogs.com/lovejune/p/12381596.html
Copyright © 2011-2022 走看看