2.4 Java快速排序
额。。基本不会Java,想看这部分的可以去看一下书里的部分,也不长。
2.5 大O记法
我们常需要描述特定算法相对于n(输入元素的个数 )需要做的工作量。在一组未排序的数据中检索,所需的时间与n成正比;如果是对排序数据用二分检索,花费的时间正比于log(n)。排序时间可能正比于n^2或者nlog(n)。
我们需要有一种方式,用它能把这种说法弄得更精确,同时又能排除掉其中的一些具体的细节(CPU速度等等)。同时,我们还想让这种方法能在不同电脑系统、不同编译系统等等复杂而不同的情况下比较待测试算法的运行时间和空间。
为了实现这个目的,人们提出了“大O记法”。在这种描述中,基本参量是n;O代表着量级(order)。比如说,二分检索是O(logn)的,也就是说它需要“通过logn量级的步骤去检索一个规模为n的数组”。记法O(f(n))表示,当n增大时,算法的运行时间最多会以正比于f(n)的速度进行增长。当然,在使用记法进行时间比较时,我们要保证待比较算法中n的大小是在同一起跑线的,这样才有比较的意义。我们把书中的一张图片截取下来来进行对记法的说明:(图源《程序设计实践》)
2.6 可增长数组
理论上来说,这一节完全可以用C++中的vector来实现,但是导师说复杂的C代码可以用来头脑风暴,使大脑更加清醒。那么我就来尝试写一下基于结构体的可增长数组。
在程序中,我们绝大多数时间所需要的数组都是动态的,数组的大小需要根据我们的需要进行更改。我们常常需要记录一个包含某些东西的变量的变化情况,在这里数组仍然是一种可能选择。为减小重新分配的代价,数组应当以成块方式重新确定大小。为了清楚起见,维护数组所需要的信息必须与数组放在一起。在C++和Java的标准库里可以找到具有这种性质的类。在C里我们可以通过struct实现类似的东西。
在C的代码中,我们要实现:
1.数组可以储存字符串
2.数组在装满时可以自动的更改大小,同时不能丢掉之前储存的数据
3.数组可以对其中的一个数据进行删除
4.删除之后数组需要向前进位
为了实现以上要求,我们使用结构体Nameval储存字符串,使用结构体NVtab储存数组信息,使用addname添加数组新的元素,使用delname进行删除。
对于下面的代码,更多的是回忆与训练大脑。其中一个地方,nvp分配地址那里,要强调的是,不使用nvtab.nameval当作头地址是因为避免在内存分配失败时丢失之前的数据。
下面就是代码,不清楚的地方我打了注释(虽然注释不一定对,欢迎指正)
/*定义一个元素类型为Nameval的可增长型数组*/
#include "stdafx.h"
#include "stdlib.h"
#include "string.h"
#include <iostream>
using namespace std;
/*应该是要加入到可增加数组中的数字或者字符串*/
typedef struct Nameval Nameval;
struct Nameval
{
char *name;
//int value;
};
/*用来记录可增加数组当前状态的结构体?*/
/*结构体专门用处?从不作为定义使用?*/
struct NVtab
{
int nval; /*数组的标号?*/
int max; /*可容纳的最大数量的Nameval类型数组数量?*/
Nameval *nameval; /*指向Nameval类型数组头地址指针?*/
}nvtab;
/*“NVINIT”为在第一次分配数组的时候,内存块可以储存的数组的数量*/
/*“NVGROW”为数组满了之后,数组扩大为原来倍数的权重系数*/
enum { NVINIT = 1, NVGROW = 2 };
/*添加元素*/
int addname(Nameval newname)
{
Nameval *nvp;
/*第一次输入Nameval类型数组?*/
/*nvtab中指针还没有被定义?*/
if (nvtab.nameval == NULL)
{
/*为Nameval分配第一个空间?*/
/*从零开始的分配空间?*/
nvtab.nameval = (Nameval *)malloc(NVINIT * sizeof(Nameval));
if (nvtab.nameval == NULL)
{
return -1;
}
/*数组角标号码?,数组都是从0开始标号*/
/*nvtab中max表示为*/
/*以指针nameval指向的序列的头地址可以存储多少个Nameval类型的数组?*/
nvtab.max = NVINIT;
nvtab.nval = 0;
}
/*如果发现将要储存的数组数量要大于等于分配内存的最大值时*/
else if (nvtab.nval >= nvtab.max)
{
/*我们接着上一个分配的内存块继续分配内存,大小是之前的二倍*/
/*注意,这里使用nvp作为分配的头地址是一个trick*/
nvp = (Nameval *)realloc(nvtab.nameval, (NVGROW * nvtab.max) * sizeof(Nameval));
if (nvtab.nameval == NULL)
{
return -1;
}
nvtab.max *= NVGROW;
nvtab.nameval = nvp;
}
/*这里的地址加1相当于在物理地址上加上了1 * sizeof(Nameval)*/
nvtab.nameval[nvtab.nval] = newname;
return nvtab.nval++;
}
/*删除元素*/
int delname(char *name)
{
int i;
for (i = 0; i < nvtab.nval; i++)
{
if (strcmp(nvtab.nameval[i].name, name) == 0)
{
/*内存块转移*/
memmove(nvtab.nameval + i, nvtab.nameval + i + 1, (nvtab.nval - (i + 1) * sizeof(Nameval)));
nvtab.nval--;
return 1;
}
}
}
void print_array()
{
for (int i = 0; i < nvtab.nval; i++)
{
cout << nvtab.nameval[i].name << endl;
}
}
int main()
{
Nameval sample_1, sample_2;
sample_1.name = "abc";
sample_2.name = "def";
addname(sample_1);
addname(sample_2);
print_array();
return 0;
}