这可能是我见过最详细的快速排序!

关于快速排序,网上,和维基都有完成的解释,他们都是。。。。。。,俺觉得都是,太过于总结话语在概述一些东西;

而我却从最本质的东西,一步一步的深入;在深入的学习过程中,我得到如下非代码层面上的感悟;

A.一个完整的模式(代码或者其他东西)都是通过没有=>part0=>part1=>part2>version0=>viersion1.......versionN=>perfect  最后达到一个完成的形式;

如果你只是去学习和体会最后的那一个完成的版本,而没有经历整个探索的过程,你的体会也不会太多;

 

B.有时候要学会抽象化和跳跃化的去思考问题,而不是step by step(这有点不具体,下次我结合具体的实例来解释)

 

C.帮助别人解决问题;并不是直接给出问题的答案或者结果;而是给别人一些引导和资源,让他自己去解决;(这点来自使用stackoverflow提问的感受)

 

D.还有第四点,要学会从特殊(具体)到一般(抽象)的探索过程;比如,我们可以先总结出满足大于零的规律,然后是小于零的规律,然后是等于零的规律,这些都是特殊(或者你可以理解成具体);从这些特殊规律总结出一般性(通用性)的规律;

 

E.还有当研究出了算法之后,要学会如何去验证自己的一般性规律,在我们的code,就是要学会写测试用例,进行验证;

 

好了,废话不多了;直接从代码开始;开始之前,想尝试做下面的practice;

 

1.选择数组的第一个data(target)将数组分成两部分;满足:   minArr<target<=maxArr;

 

2.选择数组的第一个data(target)将数组分成两部分;满足:   minArr<target<=maxArr; 并将target 放入数组“中间位置“;满足:  target greater than left data and less than right data;

 

3.找出value 在有序数组中的位置;

 

4.找出value 在无序数组中的位置;这个位置应该满足,它在有序数组中应该具有的位置;greater than left data and less than right data;

 

5.在不开辟内存的情况下(使用新的数组来存储值)完成问题2;

 

6.关于递归的学习和使用(具体看这篇文章的后半部分:http://www.cnblogs.com/mc67/p/5114008.html)

 

7.三种方式来实现我们的额快速排序;(本质的区别只有两种)

 

8.通过Unit Test 来验证我们的代码;

 

 

关于前面的 5个问题,我就直接上代码;你细细品味(建议在没有看代码前,完成上面的问题)

 

 /// <summary>/// 最基本的;将数组分成两部分;一部分比target小,一部分比target大;/// </summary>/// <param name="arr"></param>static void SplitArray(int[] arr){int len = arr.Length;int target = arr[0];List<int> min = new List<int>(len); //为了方便,这里我使用ListList<int> max = new List<int>(len); //为了方便,这里我使用Listfor (int i = 1; i < len; i++) //从第二个元素开始查找;
            {if (arr[i] < target){min.Add(arr[i]);}else{max.Add(arr[i]);  // as euqual condition,I put it into max;
                }}}/// <summary>/// split the arr and put the target in the right position(greater than left ,less than right)/// </summary>/// <param name="arr"></param>static void SplitArray1(int[] arr){int len = arr.Length;int target = arr[0];List<int> min = new List<int>(len); //为了方便,这里我使用ListList<int> max = new List<int>(len); //为了方便,这里我使用Listfor (int i = 1; i < len; i++) //从第二个元素开始查找;
            {if (arr[i] < target){min.Add(arr[i]);}}//put it in the last of min, that can make sure target greater than min(left)
            min.Add(target);for (int i = 1; i < len; i++) //从第二个元素开始查找;
            {if (arr[i] > target){max.Add(arr[i]);}}min.AddRange(max); //that can make sure  min<target<max
        }/// <summary>/// optimze the SplitArray1/// 上面的代码,还是有问题的,如果有重复的值,那么,将在判断max的时候丢掉;/// 那么问题来了,如果相等的如何判断呢;/// </summary>/// <param name="arr"></param>static void SplitArray2(int[] arr){int len = arr.Length;int target = arr[0];List<int> min = new List<int>(len); //为了方便,这里我使用ListList<int> max = new List<int>(len); //为了方便,这里我使用Listfor (int i = 1; i < len; i++) //从第二个元素开始查找;
            {if (arr[i] < target){min.Add(arr[i]);}}//put it in the last of min, that can make sure target greater than min(left)
            min.Add(target);for (int i = 1; i < len; i++) //从第二个元素开始查找;
            {if (arr[i] >= target) //我们把等于符号加上,就解决问了???? 对于本例子,是从arr[0] 开始的;//那如果是从别的位置开始呢;
                {max.Add(arr[i]);}}}/// <summary>/// start from random index;/// </summary>/// <param name="arr"></param>static void SplitArray3(int[] arr){int len = arr.Length;int randomIndex = 2;int target = arr[randomIndex]; //假设,我们从index=2 开始,这里,我们肯定满足arr.length>=2;
List<int> min = new List<int>(len); //为了方便,这里我使用ListList<int> max = new List<int>(len); //为了方便,这里我使用Listfor (int i = 0; i < len; i++) //这里,我们就要从0 开始了,遍历整个数组;
            {if (arr[i] < target){min.Add(arr[i]);}}min.Add(target);for (int i = 0; i < len; i++) //这里,我们就要从0 开始了,遍历整个数组;
            {if (arr[i] >= target && i != randomIndex) //加上这个条件,我们就可以过滤到我们的 randomIndex 避免重复添加的问题; 
                {max.Add(arr[i]);}}//最后,我们的数组,就是符合我们要求的数组;
        }//上面的做法;可以满足  min<target<=max/// <summary>/// 我们来找出元素所在的位置 index 首先是我们的有序数组中/// 通过值相等来进行判断的话,可以满足;/// 不管值有序 还是 无序的值;/// </summary>static void FindIndex(int[] arr, int value){int len = arr.Length;int index = -1;for (int i = 0; i < len; i++){if (arr[i] == value){index = i;break;}}}/// <summary>/// 两种思维方式,/// 两种思维方式;第二种,更接近普通人的表达方式;/// </summary>/// <param name="arr"></param>/// <param name="value"></param>static void FindIndex1(int[] arr, int value){int len = arr.Length;int index;bool isExist = false;for (int i = 0; i < len; i++){if (arr[i] == value){index = i;isExist = true;break;}}if (isExist == false) { index = 1; }}/// <summary>/// 上面的方法是找到,value,在数组中的第一个index 值; 不管它是否有序;/// 现在我们要找一个元素,在有序列表中的(应该插入的值,但是我们不插入)/// </summary>static void FindIndex2(int[] arr, int value){int len = arr.Length;int index = -1;for (int i = 0; i < len; i++){//你可以这么想;//如果找到大于大的数就停止;否则就继续if (arr[i] >= value)  //考虑到取等情况;
                {index = i; //这个时候,我们的数据,就可以插入在改元素的的后面;index = index - 1;  //这样就返回了可以直接插入的位置;//并且停止我们的循环;break;    //前提是有序的数组列表中
                }}//在插入的时候,就必须把后面的元素往后面挪动;//插入的时候,
        }/// <summary>/// 上面的方法是找到,value,在数组中的第一个index 值; 不管它是否有序;/// 现在我们要找一个元素,在有序列表中的(应该插入的值,但是我们不插入)///  2 和 3 两种不同的想法,写出来的code 就不太一样;/// </summary>static void FindIndex3(int[] arr, int value){int len = arr.Length;int index = -1;for (int i = 0; i < len; i++){//我们可以可以这么想; if (arr[i] <= value){index++;  //小于它的值,我们的index 就 keep move forward;  还没考虑,我们取等的情况滴呀;
                }else{//遇到,不满足的情况,我们就退出//原本的我们的index 是落后于我们的i,出去的时候;再加一次,index++;break;}}//这样就找到了我们的index;在一个可以插入的位置;
        }/// <summary>/// 先这样来想,找出小于 value的count,那么value在的位置应该就是在我们的count+1;/// </summary>/// <param name="arr"></param>/// <param name="value"></param>static void FindCountLessThan(int[] arr, int value){int len = arr.Length;int count = 0;for (int i = 0; i < len; i++){if (arr[i] <= value){count++;}}//这样,我们就能够找出小于count的数量;那么 我们value所在的位置就是我们count+1
        }/// <summary>/// 关键的来了:找到value 应该在的位置;在一个无序的数组中;/// </summary>/// <param name="arr"></param>/// <param name="value"></param>static void FindIndexInUnSortArr(int[] arr, int value){int len = arr.Length;int rightPosition = 0;  //初始化,默认我们的指针在0位置;for (int i = 0; i < len; i++){//通过遍历来查找;if (arr[i] <= value){rightPosition++;}}}/// <summary>/// 找到index在的位置,并将小于value的数据放在左边,大于value的数据放在右边/// </summary>/// <param name="arr"></param>/// <param name="value"></param>static void FindeAndSwap(int[] arr){int len = arr.Length;int middleIndex = 1;int value = arr[0];//target;// i 能找到一个小于value的值;//middleIndex 始终指向一个大于或等于 value的值;for (int i = 1; i < len; i++){if (arr[i] <= value) //当找到一个小于value的值之后,进行交换,和那个值进行交换呢;(原则:小的值移动到左边,大的值移动到右边;){                    //现在,我们找到了小的值,那么大的值呢;????if (i == middleIndex){//不进行交换; keep move
                    }else{Swap(arr, middleIndex, i);}middleIndex++;}}//最后出来后,我们要将第一个元素和中间的元素进行交换,也就是讲value放在middle的位置;Swap(arr, 0, middleIndex - 1);}/// <summary>///  上面的代码,基本上已经满足了我们额基本要求;///  完美的代码,解决了这个问题;/// </summary>/// <param name="arr"></param>static int FindeAndSwap1(int[] arr, int start, int end){int middlePosition = start + 1;int pivot = arr[start];for (int i = start + 1; i <= end; i++){if (arr[i] <= pivot){if (i == middlePosition){//there is need to swap 
                    }else{Swap(arr, middlePosition, i);}middlePosition++;}}//put the arr[start] in "middle position"(greater than left,less than right)int position = middlePosition - 1;Swap(arr, arr[start], position);return position;}/// <summary>/// 同样,我们又第二种方式来实现,/// </summary>/// <param name="arr"></param>/// <param name="low"></param>/// <param name="high"></param>static void FindeAndSwap2(int[] arr, int low,int high){int l = low-1;  //make sure pointer move firstly (before take value from arr to compare with pviot)int h = high+1; //make sure pointer move firstly (before take value from arr to compare with pviot)int pviot = arr[low]; while (l<h){while (arr[--h]> pviot) //find some value less than pvoit
                {}while (arr[++l] <= pviot) //find some value greater than pvoit   we use <= instead of <;beacause we don't let arr[start] swap 
                {}if (l < h){Swap(arr, h, l);  //swap
                }}            Swap(arr, low, h);    //put the povit in the "middle" Position
}/// <summary>/// 同样,我们也有第三种写法;/// </summary>/// <param name="arr"></param>/// <param name="low"></param>/// <param name="high"></param>static int FindeAndSwap3(int[] arr, int low, int high){int l = low;int h = high;int pviot = arr[low];while (l < h){while (arr[h] >= pviot) //chose >= instead of >; beca if the first value arr[h] equal  pviot ,this will enter endless loop;(don't enter {} do h--;)
                {h--;}while (arr[l] <= pviot)  //we chose <= instead of < to make sure pviot don't take part in swap, we will swap in the last step with "middle" position
                {l++;}if (l >= h)break;Swap(arr,l,h);}int middlePosition = h;Swap(arr,low, middlePosition);return middlePosition;}/// <summary>/// 当然,就有了,我们的第四种方法;/// 你会发现,前面的方法都是,先找到一个小于的index  high 然后找到一个大于的index low/// 然后进行交换;/// 然后有没有其他的方式呢? /// 答案是有的;/// 而且,你会发现,我们的额pvoit 是没有参与swap的,直到我们的最后一步,然后将起放在 middle position(这一步,是不可避免滴呀)/// </summary>/// <param name="arr"></param>/// <param name="low"></param>/// <param name="high"></param>/// <returns></returns>static int FindeAndSwap4(int[] arr, int low, int high){int pviot = arr[low];while (low < high){while(arr[high]>=pviot && low < high){high--;}//一旦,找到了,我们就替换;arr[low] = arr[high];  //这样,会覆盖我们的第一个值,不过,在最后,我们会将第一个值,放在“中间”位置;while (arr[low] <= pviot && low < high){low++;} //这样做的话,在没有,进行到最后一步,数组中会有一个重复的值,不过,我们最后将被我们pviotarr[high] = arr[low];}arr[low] = pviot;return high;}//到了这一步,我们的基本核心的单元,算是基本基本完整了;//然后,我们这里,再实现,三个版本的快速排序;方法;/// <summary>/// 交换/// </summary>/// <param name="arr"></param>/// <param name="i"></param>/// <param name="j"></param>static void Swap(int[] arr, int i, int j){int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}

 

 1.然后是快速排序的第一种实现方式:(从两端申明指针然后,开始查找; 这个算法是有问题的:to do.....做单元测试来一步一步的发现问题)

        /// <summary>/// 第一种实现方式,从两边开始查找;/// 还有一个值得考虑问题就是,等于的数据,如何处理呢;从我们的代码看出,等于pviot的数据,没有参与到交换中;/// 第一次交换之后将流在我们的 两端;参与下一次的交换;/// </summary>/// <param name="arr"></param>/// <param name="low"></param>/// <param name="high"></param>/// <returns></returns>static int QuickSortUnit(int[] arr, int low, int high){
int pviot=arr[low]; //we chose the first value as pivot;
while (low < high){while (arr[high] >= pviot && low<high) //>= instead of > because: if the arr[high] less than pviot,that will result in endless loop(high index never do high--;) {high--; //start search from high address }while (arr[low] <= pviot && low<high) // <= instead of < becuase:we can make sure arr[first] did't involve the whole swap until the ending {low++; //start search from low address }//we both chose equal,after first loop,the equivalent will remianing the origial positon and take part in next sort; that does't affect the final result;if (low >= high)break;Swap(arr, low, high);}//because we firstly star from high; so the last value that must less than pviot;so high is our "middle" position;int middlePosition = high; //make the code easy to read; Swap(arr,middlePosition,low);return high;}/// <summary>/// recurion to resolve the same problem;/// </summary>/// <param name="arr"></param>/// <param name="low"></param>/// <param name="high"></param>static void QuickSort(int [] arr,int low,int high){if (low >= high) //make sure recusion can stopreturn;int middlePositon=QuickSortUnit(arr,low,high);//star from left;QuickSort(arr,low, middlePositon-1);//star from rightQuickSort(arr, middlePositon+1,high);}

2.然后是快速排序的第二种实现方式(本质上和第一种方式一样的,只不过写法(想法),略有不同,一旦找到元素就开始覆盖指定位置的值,这样每次都会有一个重复的值,不过这个重复的值,最后会被我们的pivot给占据)

        /// <summary>/// /// </summary>/// <param name="arr"></param>/// <param name="low"></param>/// <param name="high"></param>static int QuickSortUnit(int[] arr, int low, int high){int pviot = arr[low];while (low < high){while (arr[high] >= pviot && low<high){high--;}arr[low] = arr[high]; //once find the value less than pviot ,put it in low address;while (arr[low] <= pviot && low < high){low++;}arr[high] = arr[low]; //once find the value greater than pviot, put it in high address;
            }int middlePositon = high;arr[low] = pviot; return middlePositon;}/// <summary>/// use recursion to resovle the same problem;/// </summary>/// <param name="arr"></param>/// <param name="low"></param>/// <param name="high"></param>static void QuickSort(int [] arr,int low,int high){if (low >= high)return;int middlePosition = QuickSortUnit(arr,low,high);QuickSortUnit(arr, low, middlePosition-1);QuickSortUnit(arr, middlePosition+1, high);}

思考:

   最后一步交互为哈一定要取low呢;(debug 一下这个:    var arr = new[] { 7, 8, 7 };)

 

 

第三种方式;其实,本质是一样,不过,这种方式,不用从两端申明pointer,去查找(伴随着两个while循环);这里我们用一个循环,还是两个pointer,不过他们的出发位置,就有所不同了,都从左边开始;

 提现了不同的思路去解决问题;

 

 

 最后一种,我们就直接给链接吧;

 https://www.hackerearth.com/zh/practice/algorithms/sorting/quick-sort/tutorial/

非常好的网站;

 

这里,我们再来总结一下,整个执行过程:

1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换;
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;
5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-

最后,就是要通过写单元测试,来assert我们的值;

 

转载于:https://www.cnblogs.com/mc67/p/8259734.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/252542.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LINQ to SQL 实现 GROUP BY、聚合、ORDER BY

前言 本示例主要实现 LINQ 查询&#xff0c;先分组&#xff0c;再聚合&#xff0c;最后在排序。示例很简单&#xff0c;但是使用 LINQ 却生成了不同的 SQL 实现。 1) 采用手动编写 SQL 实现 SELECT ROW_NUMBER() OVER(ORDER BY T.RealTotal DESC) AS SN, * FROM ( SELECT (S…

Mysql之事务

什么是事务 假如你下了一笔订单&#xff0c;会有以下数据库操作&#xff1a; 1. 生成一笔订单记录 2. 减少库存 3. 从你的账户中减少金额 4. 生成支付记录 这边的四个操作缺一不可&#xff0c;一旦某一个操作出现异常&#xff0c;则全部操作都需要全部回滚。而事务的作…

JVM垃圾回收机制总结

对于垃圾回收机制我先抛出三个问题&#xff1a; ①哪些内存需要回收&#xff1f; ②什么时候回收&#xff1f; ③如何回收&#xff1f; 下面我们主要针对这三个问题来研究JVM GC 一、哪些内存需要回收&#xff1f; 1.JAVA使用可达性分析法来判断对象是否需要回收。 这个算法的基…

虚拟机增加内存方法

楼主由于要在虚拟机里面装一个oracle&#xff0c;在安装过程中&#xff0c;提示物理内存不符合最低标准&#xff0c;如图1. 图1 因为懒得新建一个虚拟机了&#xff0c;所以考虑给虚拟机新增磁盘空间。 首先说明&#xff0c;新增磁盘空间&#xff0c;必须保证当前虚拟机里每个新…

myBatis xml if、where、if-else?、foreach 心得

MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验&#xff0c;你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格&#xff0c;还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这…

bzoj千题计划213:bzoj2660: [Beijing wc2012]最多的方案

http://www.lydsy.com/JudgeOnline/problem.php?id2660 很容易想到是先把n表示成最大的两个斐波那契数相加&#xff0c;然后再拆分这两个斐波那契数 把数表示成斐波那契进制的形式&#xff0c;第i位表示有没有第i个斐波那契数 比如16133 001001 那么拆分一个数就是把一个1…

面对对象-封装

private 私有的 package/friendly/default 不写 protected 受保护的 public 公共的 那么什么情况该用什么修饰符呢&#xff1f;从作用域来看&#xff0c;public能够适用所有的情况。 但是大家在工作的时候&#xff0c;又不会真正全部都适用public,那么到底什么情况改用什么修饰…

本文详解5G是个什么鬼,程序员都准备好了吗?

无线移动通讯发展历史 最近5G的概念炒的如火如荼&#xff0c;为此&#xff0c;华为和高通还干了一仗。这篇文章从技术层面给大家分析&#xff0c;什么是5G&#xff0c;它和4G比&#xff0c;高级在哪里&#xff1f; 我们来看看移动互联网的技术发展&#xff1a; 然后我们在来看看…

安装kerberos报错 error: command 'gcc' failed with exit status 1

pip install kerberos 报错&#xff1a;error: command gcc failed with exit status 1 安装环境工具 yum install gcc libffi-devel python-devel openssl-devel 再次安装kerberos安装成功 转载于:https://www.cnblogs.com/panbc/p/8268574.html

jquery正则表达式验证:验证全是数字

需求说明&#xff1a; 前端页面使用正则表达式验证输入框中输入的内容全是数字。 代码说明&#xff1a; 这里只介绍正则表达式&#xff0c;其他部分的代码不做介绍。如果有其他需要自行修改即可。 步骤一&#xff1a;建立一个页面可以是html、jsp等&#xff0c;引入jquery-3.2.…

0613课堂汇总

一&#xff1a; 数据类型&#xff1a; 基本数据类型{byte(8)/short(16)/char(16)/int(32)/float(32)/long(64)/double(64)/boolean(1)}引用数据类型{*除了基本数据类型都是引用数据类型 *包括API中的类&#xff08;String,File&#xff09;*自定义的类&#xff08;Personal A…

Istio流量管理实践之(5): 使用cert-manager部署Istio自定义入口网关及进行证书管理...

Istio Gateway提供多个自定义入口网关的支持能力&#xff0c;通过开放一系列端口用于承载网格边缘的进入连接&#xff0c;同时可以使用不同loadbalancer来隔离不同的入口流量。cert-manager可用于使用存储在Kubernetes Secret资源中的任意签名密钥对来获取证书。本文提供了手动…

log4配置

log4j 和 log4j2 方式一&#xff1a;log4j2.xml 添加 jar 包 1 <!-- log4j-core --> 2 <!-- 3 <dependency> 4 <groupId>org.apache.logging.log4j</groupId> 5 <artifactId>log4j-core</artifactId> 6 …

[bzoj3625][Codeforces 250 E]The Child and Binary Tree(生成函数+多项式运算+FFT)

3625: [Codeforces Round #250]小朋友和二叉树 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 650 Solved: 283[Submit][Status][Discuss]Description 我们的小朋友很喜欢计算机科学&#xff0c;而且尤其喜欢二叉树。考虑一个含有n个互异正整数的序列c[1],c[2],...,c[n]。…

常用内建模块

一.datetime 1.模块导入: from datetime import datetime 2.获取当前日期和时间: >>> now datetime.now() >>> print(now) 2019-01-13 14:19:38.1810003.获取指定日期和时间: >>> dt datetime(2019,1,10,15,0) >>> print(dt) 2019-01-10…

子序列进阶问题

题目&#xff1a; 有一个数组&#xff0c;让找到两个不重复的连续子序列A,B &#xff0c;求Max(Sum(A)-Sum(B) 分析&#xff1a; AB必定连续&#xff0c;设两端连接处index为{X&#xff0c;x1}&#xff0c;X可取0~n-1 设F(x)为连接处index为{X&#xff0c;x1}时 Max(Sum(A)…

day5-shelve模块

一、概述前面章节我们讲述了json和pickle模块的序列化和反序列化处理&#xff0c;他们有一个不足是在python 3中不能多次dump和load&#xff0c;shelve模块则可以规避这个问题。shelve模块是一个简单的k,v将内存数据通过文件持久化的模块&#xff0c;可以持久化任何pickle可支持…

程序员:请你不要对业务「置之不理」

成长是条孤独的路&#xff0c;一个人会走得更快&#xff1b;有志同道合者同行&#xff0c;会走得更远。本篇内容整理自 21 天鲲鹏新青年计划线上分享内容。鲲鹏新青年计划是由 TGO 鲲鹏会组织的线上分享活动&#xff0c;希望能帮助更多同学一起学习、成长。12 月 28 日&#xf…

在Ubuntu系统下如何将chrome浏览器的bookmarks导出到本地

1. 打开chrome浏览器在页面的右上角点击那个三个小点的位置&#xff0c;找到bookmarks&#xff0c;然后点击bookmarks manager,然后在organize右侧大倒三角下选择&#xff0c;export bookmarks to HTML&#xff0c;选择要保存的位置&#xff0c;利用同样的方法下次就可以直接导…

php基于数组的分页实现

关于数组的分页函数,用数组进行分页的好处是可以方便的进行联合多表查询,只需要将查询的结果放在数组中就可以了以下是数组分页的函数,函数page_array用于数组的分页&#xff0c;函数show_array用于分页函数的操作及显示&#xff0c;需要配合使用.两个函数通过全局变量$countpa…