Abstract
本人正在学习Stanford CS107。这门课很不错,我简单把这门课的信息说一下。
key | value |
---|---|
官网 | https://see.stanford.edu/Course/CS107 |
课程名 | 编程范式 |
所需基础 | C,C++,数据结构与算法 |
上课视频 | Bilibili搜索 https://www.bilibili.com/video/av9789206 (可能会失效) |
我做的作业 | https://github.com/peanwang/stanford-cs107 |
Assignment Task
这次的作业也分为了两个部分。主要会使用下面几个函数:
内存管理
malloc
realloc
free
memcpy
memmove
功能性函数
qsort
lsearch
bsearch
Task I: The C vector
第一个任务是实现C vector。其算法和C++的vector使用的是一样的,都是均摊。
在handout里老师给出了为什么选择C vector,而不是C++的vector
①C++ 的vector会占用大量内存。对于嵌入式系统来说,C vector就比C++ vector适合。(C++ vector每有一种数据类型,都要一份代码副本,会导致代码膨胀)
②C vector会比C++ vector快
这次的任务就是实现vector。声明vector结构体和实现下面的函数。并通过老师写的测试程序。
typedef int (*VectorCompareFunction)(const void *elemAddr1,const void *elemAddr2);
typedef void (*VectorMapFunction)(void *elemAddr, void *auxData);
typedef void (*VectorFreeFunction)(void *elemAddr);
typedef struct {// implementation specific... you decide this } vector;
void VectorNew(vector *v, int elemSize,VectorFreeFunction freefn, int initialAllocation);
void VectorDispose(vector *v); int VectorLength(const vector *v);
void *VectorNth(const vector *v, int position);
void VectorInsert(vector *v, const void *elemAddr, int position);
void VectorAppend(vector *v, const void *elemAddr);
void VectorReplace(vector *v, const void *elemAddr, int position);
void VectorDelete(vector *v, int position);
int VectorSearch(const vector *v, const void *key,VectorCompareFunction searchfn, int startIndex,bool isSorted);
void VectorSort(vector *v, VectorCompareFunction comparefn);
void VectorMap(vector *v, VectorMapFunction mapfn, void *auxData);
Task I: The C hashset
使用Task Ⅰ中C vector,声明hashset的结构体和实现下面的函数。
而且handout里有说要充分利用vector的函数。因为
①会让工作很有趣
②not re-invent the wheel.不要重复造轮子
typedef int (*HashSetHashFunction)(const void *elemAddr, int numBuckets);
typedef int (*HashSetCompareFunction)(const void *elemAddr1,
const void *elemAddr);
typedef void (*HashSetMapFunction)(void *elemAddr, void *auxData);
typedef void (*HashSetFreeFunction)(void *elemAddr);
typedef struct {
// implementation specific… you decide this as well
} hashset;
void HashSetNew(hashset *h, int elemSize, int numBuckets,
HashSetHashFunction hashfn, HashSetCompareFunction comparefn,
HashSetFreeFunction freefn);
void HashSetDispose(hashset *h);
int HashSetCount(const hashset *h);
void HashSetEnter(hashset *h, const void *elemAddr);
void *HashSetLookup(const hashset *h, const void *elemAddr);
void HashSetMap(hashset *h, HashSetMapFunction mapfn, void *auxData);
Assignment solution
Task I: The C vector
说实话:和老师上课讲的stack很像,照着模仿就行了。
我说下lsearch。我还以为lsearch和老师上课讲的是一样的。但是 man lsearch,发现有一些差别。
首先:lsearch和lfind都在search.h里,其函数签名如下:
void *lfind(const void *key, const void *base, size_t *nmemb,
size_t size, int(*compar)(const void *, const void *));
void *lsearch(const void *key, void *base, size_t *nmemb,
size_t size, int(*compar)(const void *, const void *));
其第三个参数居然是个指针。o_o …
lsearch和lfind都是线性搜索,但是
lfind如果成功找到,返回其指针。失败返回NULL。
lsearch如果成功找到,返回其指针。如果失败,会将查找的元素添加到数组末尾,并返回其指针。
很显然:我们需要的是lfind,而不是lsearch,老师的FAQ也提到了这个
vector结构体
typedef struct {
void* elems;
int elemsize;
int logicallen;
int alloclen;
VectorFreeFunction freefn;
} vector;
函数实现
static void VectorGrow(vector *v){
v->alloclen *= 2;
v->elems = realloc(v->elems, v->alloclen*v->elemsize);
}
void VectorNew(vector *v, int elemSize, VectorFreeFunction freeFn, int initialAllocation){
assert( elemSize>0);
assert( initialAllocation>=0);
if( initialAllocation==0)
initialAllocation = 4;
v->elemsize = elemSize;
v->logicallen = 0;
v->alloclen = initialAllocation;
v->elems = malloc( elemSize*v->alloclen);
assert(v->elems != NULL);
v->freefn = freeFn;
}
void VectorDispose(vector *v){
if(v->freefn !=NULL){
for(int i=0;i<v->logicallen;i++)
v->freefn((char*)v->elems+i*v->elemsize);
}
free(v->elems);
}
int VectorLength(const vector *v){
return v->logicallen;
}
void *VectorNth(const vector *v, int position){
assert(position>=0);
assert(v->logicallen>position);
return (char*)v->elems+position*v->elemsize;
}
void VectorReplace(vector *v, const void *elemAddr, int position){
assert(position>=0);
assert(v->logicallen>position);
char* replace = VectorNth(v,position);
memcpy(replace, elemAddr, v->elemsize);
}
void VectorInsert(vector *v, const void *elemAddr, int position){
assert(position>=0);
assert(v->logicallen>=position);
if ( v->logicallen == v->alloclen)
VectorGrow(v);
if (v->logicallen == position)
VectorAppend(v, elemAddr);
else{
char *front = VectorNth(v, position);
char* end = front + v->elemsize;
int movesize = v->elemsize*(v->logicallen-position);
char* buffer = (char*)malloc(movesize);
memcpy(buffer, front, movesize);
memcpy(front, elemAddr, v->elemsize);
memcpy(end, buffer, movesize);
free(buffer);
v->logicallen++;
}
}
void VectorAppend(vector *v, const void *elemAddr)
{
if ( v->logicallen == v->alloclen)
VectorGrow(v);
v->logicallen++;
void *target = VectorNth(v,v->logicallen-1);
memcpy(target, elemAddr, v->elemsize);
}
void VectorDelete(vector *v, int position){
assert(position>=0);
assert(v->logicallen>position);
char *target = VectorNth(v,position);
if(v->freefn !=NULL){
v->freefn(target);
}
if(v->logicallen-1 != position){ //the last one
int end_size = v->logicallen-position-1;
for(int i=0;i<end_size;i++){
char* next = target + v->elemsize;
memcpy(target, next, v->elemsize);
target = next;
}
}
v->logicallen--;
}
void VectorSort(vector *v, VectorCompareFunction compare){
assert(compare != NULL);
qsort(v->elems, v->logicallen, v->elemsize, compare);
}
void VectorMap(vector *v, VectorMapFunction mapFn, void *auxData){
assert(mapFn != NULL);
for(int i=0;i<v->logicallen;i++){
char *target = (char*)v->elems + i*v->elemsize;
mapFn(target, auxData);
}
}
static const int kNotFound = -1;
int VectorSearch(const vector *v, const void *key, VectorCompareFunction searchFn, int startIndex, bool isSorted){
assert(startIndex>=0);
assert(v->logicallen>=startIndex);
assert(key != NULL);
assert(searchFn != NULL);
char* result;
char* base = (char*)v->elems + startIndex*v->elemsize;
if(isSorted)
result = bsearch(key, base, v->logicallen-startIndex, v->elemsize, searchFn);
else{
size_t size = v->logicallen-startIndex;
result = lfind(key, base, &size, v->elemsize, searchFn);
}
return result==NULL ?kNotFound :(result-(char*)v->elems)/v->elemsize;
}
完成测试
Task II: The C hashset
使用Task ①中实现的vector,会使hashset的实现变得很轻松。基本上没有遇到什么问题,一遍就过了。
hashset结构体
typedef struct {
vector* bucket;
int elemsize;
int elemNum;
int numBuckets;
HashSetHashFunction hashfn;
HashSetCompareFunction comparefn;
HashSetFreeFunction freefn;
} hashset;
函数实现:
void HashSetNew(hashset *h, int elemSize, int numBuckets,HashSetHashFunction hashfn, HashSetCompareFunction comparefn, HashSetFreeFunction freefn){
assert(elemSize>0);
assert(numBuckets>0);
assert(hashfn !=NULL);
assert(comparefn !=NULL);
h->elemNum = 0;
h->elemsize = elemSize;
h->numBuckets = numBuckets;
h->hashfn= hashfn;
h->comparefn = comparefn;
h->freefn = freefn;
h->bucket = (vector*)malloc(sizeof(vector)*h->numBuckets);
assert(h->bucket);
for(int i=0;i<numBuckets;i++)
VectorNew(h->bucket+i,elemSize,freefn,4);
}
void HashSetDispose(hashset *h){
if(h->freefn !=NULL){
for(int i=0;i<h->numBuckets;i++)
VectorMap(h->bucket+i, h->freefn, NULL);
}
for(int i=0;i<h->numBuckets;i++)
VectorDispose(h->bucket+i);
free(h->bucket);
}
int HashSetCount(const hashset *h){
return h->elemNum;
}
void HashSetMap(hashset *h, HashSetMapFunction mapfn, void *auxData){
assert(mapfn !=NULL);
for(int i=0;i<h->numBuckets;i++){
VectorMap(h->bucket+i, mapfn, auxData);
}
}
void HashSetEnter(hashset *h, const void *elemAddr){
assert(elemAddr !=NULL);
void* result = HashSetLookup(h, elemAddr);
if(result == NULL){
int bucket = h->hashfn(elemAddr, h->numBuckets);
assert(bucket>=0);
assert(h->numBuckets>bucket);
VectorAppend(h->bucket+bucket, elemAddr);
h->elemNum++;
}
else{
memcpy(result, elemAddr, h->elemsize);
}
}
void *HashSetLookup(const hashset *h, const void *elemAddr){
assert(elemAddr !=NULL);
int bucket = h->hashfn(elemAddr, h->numBuckets);
assert(bucket>=0);
assert(h->numBuckets>bucket);
int result = VectorSearch(h->bucket+bucket, elemAddr, h->comparefn, 0, false);
return result==-1? NULL: VectorNth(h->bucket+bucket, result);
}
完成测试
Assignment think
不知道是不是经过上次作业的折磨,这次作业写的非常快,两天就写完了,爽q(≧▽≦q)。