线性表学习笔记
最近学习数据结构的线性表,主要是借助教材《数据结构》(C语言版)那本。在课堂上学习的时候没有认真听讲,唉,,,书上面的程序没有写过,知识了解大概,现在开始准备面试,数据结构是首先要拿下的基础知识,所以开始了数据结构的再学习,下面是线性表部分,下面还会有栈、队列、串、数组、树、图、排序、查找等等。程序不一定完全正确,都是用C语言实现的书上的例子,没有包括所有的线性表性质,但基本上实现了线性表基本操作(顺序和链式),欢迎大家提出宝贵意见。
下面附上练习源码(仅作参考)
1 /** 2 * 线性表的各种操作 3 * 对应《数据结构(C语言版)》的教材 4 * @author:zhaoyafei 5 * @aime:2015-6-16 6 */ 7 //引入必要头文件 8 #include <stdio.h> 9 #include <stdlib.h> 10 //约定的宏定义 11 #define TRUE 1 12 #define FALSE 0 13 #define OK 1 14 #define ERROR 0 15 #define INFEASIBLE -1 16 #define OVERFLOW -2 17 18 //初始空间分配量 19 #define LIST_INIT_SIZE 100 20 //空间分配的增量 21 #define LISTINCREMENT 10 22 //Status是函数的类型,其值是函数结果的状态码 23 typedef int Status; 24 //数据元素约定为ElemType,可以自己定义 25 typedef int ElemType; 26 27 //线性表的顺序实现 28 typedef struct{ 29 ElemType * elem; //存储空间的基地址 30 int lenght; //当前的长度 31 int listsize;//当前分配的存储容量 32 }SqList; 33 34 //线性表的链式实现 35 typedef struct LNode{ 36 ElemType data; //存储数据 37 struct LNode * next; //递归定义,指向下一个元素 38 }LNode,*LinkList;//结构体类型指针 39 40 //双向链表的实现 41 typedef struct DuLNode{ 42 ElemType data; 43 struct DuLNode *prior; 44 struct DuLNode *next; 45 }DulNode, *DuLinkList; 46 47 /*************************顺序实现***************************/ 48 //构造空的线性表 49 Status InitList(SqList &L, int lenght){ 50 if (lenght == 0) { 51 lenght = LIST_INIT_SIZE; 52 } 53 L.elem = (ElemType *)malloc(lenght * sizeof(ElemType)); 54 55 if(!L.elem){ 56 exit(OVERFLOW); //分配存储空间失败 57 } 58 L.lenght = 0; //初始空表长度为0 59 L.listsize = lenght ;//初始存储容量为100 60 return OK; 61 } 62 63 //在第i的位置插入元素e 64 Status ListInse_Sq(SqList &L, ElemType e, int i){ 65 ElemType *p, *q; 66 if(i < 0 || i > L.lenght){ 67 //i的值不合法 68 return ERROR; 69 } 70 if (L.lenght >= L.listsize) { 71 //空间不够,重新分配存储空间 72 ElemType *newbase = (ElemType *)realloc(L.elem ,(L.listsize + LISTINCREMENT)*sizeof(ElemType)); 73 if(!newbase){ 74 return OVERFLOW; //存储分配失败 75 } 76 L.elem = newbase; //记录新基值 77 L.listsize += LISTINCREMENT; //增加存储容量 78 } 79 q = &L.elem[i]; //得到元素插入的位置 80 for (p = &L.elem[L.lenght]; p >= q; --p) { 81 *p = *(p-1); //把插入位置元素之后的元素往后移动 82 } 83 *q = e; //插入新元素e 84 L.lenght +=1; 85 return OK; 86 } 87 88 //删除第i位置元素,并用e返回其值 89 Status ListDelete_sq(SqList &L, int i, ElemType &e){ 90 int *p,*q; 91 if(i < 0 || i > L.lenght){ 92 return ERROR; //i的值不合法 93 } 94 q = &L.elem[i]; //被删除元素的位置为i; 95 e = *q; //被删除元素的值赋值给e 96 //被删除元素后的元素左移 97 for (p = q; p< (L.elem + L.lenght); p++){ 98 *p = *(p + 1); 99 } 100 --L.lenght; 101 return OK; 102 } 103 104 //得到第i个元素 105 Status GetElem(SqList &L, int i, ElemType &e){ 106 ElemType *p; 107 p = L.elem; 108 int j = 1; 109 if(i < 1){ 110 exit(OVERFLOW);//下表不合法 111 } 112 while(p && j < i){ 113 p = &L.elem[j]; 114 j++; //寻找第i个元素,同时检查p是否越界 115 } 116 if(!p || j > i){ 117 return false;//下标越界 118 } 119 e = L.elem[j-1]; 120 return OK; 121 } 122 123 //打印线性表 124 void PrintList(SqList L){ 125 for(int i = 0; i < L.lenght; i++) { 126 printf("%d ", *(L.elem + i)); 127 } 128 printf("\r\n"); 129 } 130 131 //合并两个线性表 132 void Combine(SqList La, SqList Lb, SqList &Lc){ 133 ElemType *pa, *pb, *pc; 134 Lc.listsize = La.lenght + Lb.lenght; 135 InitList(Lc, Lc.listsize); //初始化LC\pc = Lc.elem; 136 Lc.lenght = Lc.listsize; 137 pc = Lc.elem; 138 pa = La.elem; 139 pb = Lb.elem; 140 //还没有循环完 141 while (pa <= &La.elem[La.lenght -1] && pb <= &Lb.elem[Lb.lenght -1]){ 142 if (*pa <= *pb){ 143 *pc++ = *pa++; 144 }else{ 145 *pc++ = *pb++; 146 } 147 } 148 //插入La的剩余元素 149 while(pa <= &La.elem[La.lenght -1]){ 150 *pc++ = *pa++; 151 } 152 //插入Lb的剩余元素 153 while(pb <= &Lb.elem[Lb.lenght -1]){ 154 *pc++ = *pb++; 155 } 156 } 157 158 //冒泡排序法,排序线性表 159 void Sort(SqList &L){ 160 ElemType *pd1,*pd2,nums,length = L.lenght; 161 for(int num = 0; num < length - 1; num++){ 162 for(int num1 = num + 1 ;num1 < length; num1++){ 163 pd1 = &L.elem[num]; 164 pd2 = &L.elem[num1]; 165 if(*pd1 > *pd2){ 166 nums = *pd1; 167 *pd1 = *pd2; 168 *pd2 = nums; 169 } 170 } 171 } 172 } 173 174 /*************************链式实现***************************/ 175 //单链表初始化 176 Status InitList_L(LinkList &L){ 177 L = (LinkList)malloc(sizeof(LNode)); 178 if(!L){ 179 exit(OVERFLOW); //分配存储空间失败 180 } 181 L->next = NULL; //初始的头结点为空 182 return OK; 183 } 184 185 //逆向建立单链表 186 Status CreateList_L(LinkList &L, int n){ 187 L = (LinkList)malloc(sizeof(LNode)); 188 if(!L){ 189 exit(OVERFLOW); //分配存储空间失败 190 } 191 L->next = NULL; //初始的头结点为空 192 printf("请输入5个整数:\n"); 193 for(int i = n; i > 0; --i){ 194 LinkList p; 195 p = (LinkList)malloc(sizeof(LNode)); 196 scanf("%d",&p->data); 197 p->next = L->next; 198 L->next = p; 199 } 200 return OK; 201 } 202 203 //在建立好的单链表中的第n个位置插入元素e 204 Status ListInsert_L(LinkList &o, int n, ElemType e){ 205 LinkList L = o; 206 int j = 0;//记录插入点的位置 207 while(L && j < n - 1 && n > 0){ 208 L = L->next; 209 j++; 210 } 211 if(L){//合法 212 LinkList p; 213 p = (LinkList)malloc(sizeof(LNode)); 214 p->data = e; 215 p->next = L->next; 216 L->next = p; 217 return OK; 218 } 219 } 220 221 //在建立好的单链表中的第n个位置删除元素,并用e返回 222 Status ListDelete_L(LinkList &o, int n, ElemType e){ 223 LinkList L = o; 224 int j = 0; //记录插入点的位置 225 while(L && j < n - 1 && n > 0){ 226 L = L->next; 227 j++; 228 } 229 if(L && L->next){//合法 230 LinkList q; 231 q = L->next; 232 e = q->data; 233 L->next = q->next; 234 free(q); 235 return OK; 236 } 237 } 238 239 //合并链式线性表 240 void Combine_L(LinkList &La, LinkList &Lb,LinkList &Lc){ 241 LinkList pa,pb,pc; 242 pa = La->next; 243 pb = Lb->next; 244 //Lc = La;//把La作为Lc的头结点 245 pc = Lc; 246 while(pa && pb){ 247 if(pa->data <= pb->data){//判断两者得大小 248 pc->next = pa; 249 pa = pa->next; //pa指针下移 250 }else{ 251 pc->next = pb; 252 pb = pb->next; //pb指针下移 253 } 254 pc = pc->next; //pc下移一位 255 } 256 pc->next = pa ? pa : pb; 257 free(La); 258 free(Lb); 259 } 260 261 //得到第i个元素 262 Status GetElem_L(LinkList &L, int i, ElemType &e){ 263 LinkList p; 264 p = L; 265 int j = 0; 266 if(i < 1){ 267 exit(OVERFLOW);//下表不合法 268 } 269 while(p && j < i){ 270 p = p->next;//寻找第i个元素,同时检查p是否越界 271 j++; 272 } 273 if(!p || j > i){ 274 return false;//下标越界 275 } 276 e = p->data; 277 return OK; 278 } 279 280 //冒泡排序法排序单链表 281 void Sort_L(LinkList &L){ 282 LinkList p,q; 283 int num; 284 for(p = L; p != NULL; p = p->next){ 285 for(q = p->next;q != NULL; q = q->next){ 286 if(p->data > q->data){ 287 num = p->data; 288 p->data = q->data; 289 q->data = num; 290 } 291 } 292 } 293 } 294 295 //打印链表节点信息 296 void PrintfList_L(LinkList L){ 297 if(L){ 298 do{ 299 L = L->next; 300 printf("%d ",L->data); 301 }while(L->next); 302 } 303 printf("\n"); 304 } 305 306 /***********************双向链表实现*************************/ 307 //单链表初始化 308 Status InitList_Du(DuLinkList &L){ 309 L = (DuLinkList)malloc(sizeof(DulNode)); 310 if(!L){ 311 exit(OVERFLOW); //分配存储空间失败 312 } 313 L->next = NULL; //初始的头结点为空 314 L->prior = NULL; 315 return OK; 316 } 317 318 //在双向链表中第n个位置插入元素e; 319 Status ListInsert_Dul(DuLinkList &P, int n, ElemType e){ 320 DuLinkList L = P; 321 int j = 0;//记录插入点的位置 322 while(L && j < n - 1 && n > 0){ 323 L = L->next; 324 j++; 325 } 326 if(L){//合法 327 DuLinkList c; 328 c = (DuLinkList)malloc(sizeof(DulNode)); 330 c->data = e; 331 c->next = L->next; 332 L->next = c; 333 if(c->next){ 334 c->next->prior = c; 335 } 336 c->prior = L; 337 return OK; 338 } 339 } 340 341 //在双向链表中第n个位置删除元素,并用e返回数据; 342 Status ListDelete_Dul(DuLinkList &P, int n, ElemType &e){ 343 DuLinkList L = P; 344 int j = 0;//记录插入点的位置 345 while(L && j < n && n > 0){ 346 L = L->next; 347 j++; 348 } 349 if(L){//合法 350 e = L->data; 351 L->prior->next = L->next; 352 L->next->prior = L->prior; 353 //free(L); 354 return OK; 355 } 356 } 357 358 //打印双向链表的数据 359 void PrintList_Du(DuLinkList L){ 360 if(L){ 361 do{ 362 L = L->next; 363 printf("%d ",L->data); 364 }while(L->next); 365 } 366 printf("\n"); 367 } 368 369 //主方法 370 void main(){ 371 ElemType e,f; 372 int init,i,elem; 373 int TestData[8] = {9,1,8,5,7,2,1,3}; 374 int TestDataTwo[5] = {8,3,2,6,1}; 375 376 printf("*************线性表顺序实现*************\n"); 377 SqList La,Lb,Lc; 378 init = InitList(La, LIST_INIT_SIZE); 379 380 for (i = 0; i < 8; i++) {//初始化La 381 ListInse_Sq(La, TestData[i], i); 382 } 383 printf("La:\n构造后的La:"); 384 PrintList(La); 385 386 GetElem(La,3,e);//得到第3个元素 387 printf("得到La的第3个元素是:%d\n",e); 388 389 ListDelete_sq(La,3,e);//线性表的删除操作 390 printf("删除后的La:"); 391 PrintList(La); 392 393 ListInse_Sq(La,e,3);//还原数据 394 395 Sort(La);//排序 396 printf("排序后的La:"); 397 PrintList(La); 398 399 printf("Lb:\n构造后的Lb:"); 400 InitList(Lb, LIST_INIT_SIZE); 401 for (i = 0; i < 5; i++) { 402 ListInse_Sq(Lb, TestDataTwo[i], i); 403 } 404 PrintList(Lb); 405 406 Sort(Lb);//排序Lb 407 printf("排序后的Lb:"); 408 PrintList(Lb); 409 410 printf("合并La与Lb后的Lc:");//合并La,Lb 411 Combine(La, Lb, Lc); 412 PrintList(Lc); 413 414 printf("\n*************线性表链式实现*************\n"); 415 LinkList LaL,LbL,LcL,LdL; 416 //CreateList_L(LbL,5)//逆序建立单链表 417 InitList_L(LaL);//初始化单链表 418 printf("LaL:\n构造后的LaL:"); 419 for (i = 1; i <= 8; i++) {//初始化La 420 ListInsert_L(LaL, i, TestData[i-1]); 421 } 422 423 PrintfList_L(LaL);//打印单链表信息 424 425 GetElem_L(LaL,3,e);//得到第3个元素 426 printf("得到LaL的第3个元素是:%d\n",e); 427 428 ListInsert_L(LaL,3,10);//插入新元素 429 printf("插入后的LaL:"); 430 PrintfList_L(LaL); 431 432 ListDelete_L(LaL,3,e);//删除添加的新元素 433 printf("删除后的LaL:"); 434 PrintfList_L(LaL); 435 436 Sort_L(LaL);//排序 437 printf("排序后的LaL:"); 438 PrintfList_L(LaL); 439 440 printf("LbL:\n构造后的LbL:"); 441 InitList_L(LbL);//初始化单链表 442 443 for (i = 1; i < 6; i++) { 444 ListInsert_L(LbL, i, TestDataTwo[i-1]); 445 } 446 PrintfList_L(LbL); 447 448 printf("排序后的LbL:"); 449 Sort_L(LbL);//排序 450 PrintfList_L(LbL); 451 452 printf("合并La与Lb后的Lc:"); 453 InitList_L(LcL);//初始化单链表 454 Combine_L(LaL, LbL, LcL); 455 PrintfList_L(LcL); 456 457 printf("\n*************双向链表的实现*************\n"); 458 DuLinkList DuLa; 459 //初始化双向链表 460 InitList_Du(DuLa); 461 462 for (i = 1; i < 9; i++) { 463 ListInsert_Dul(DuLa, i, TestData[i-1]); 464 } 465 printf("DuLa:\n构造后的DuLa:"); 466 PrintList_Du(DuLa); 467 printf("添加新元素元素后的DuLa:"); 468 ListInsert_Dul(DuLa, 3, 10); //在第三个位置添加元素10 469 PrintList_Du(DuLa); 470 471 printf("删除新添加元素后的DuLa:"); 472 ListDelete_Dul(DuLa,3,e);//删除元素 473 PrintList_Du(DuLa); 474 475 }
运行结果:
需要补充一下知识点:C语言动态内存分配,C动态内存分配主要有这几个函数:malloc、free、calloc、realloc
malloc:原型 void *malloc(unsigned size)
size表示分配内存的大小,函数会从内存池里取一块连续的内存,返回指向内存起始位置的指针,这块内存进行初始化,需要手动初始化也可以通过calloc初始化。
注意:malloc分配的是连续的内存。可用内存小于请求,malloc向操作系统请求得到更多的内存,如果无法提供更多内存,则返回一个NULL指针。
例如:初始化线性表(构造空的线性表 )
L.elem = (ElemType *)malloc(lenght * sizeof(ElemType));
calloc:原型 void*calloc(unsigned n,unsigned size)
其中n表示需要分配内存的数据项个数,size指每个数据项的大小。malloc和calloc之间主要区别:calloc返回指向内存的指针之前把它初始化未0。 请求内存数量的方式不同。
realloc:原型:void *realloc(void*list,unsigned size)
将list所指的已分配内存区的大小改为size。realloc是修改一个原先已经分配的内存块的大小。如果原先的内存块无法改变大小,realloc将分配另一块正确大小的内存,并把原先那块内存的内容复制到新的快上。
例如:ElemType *newbase = (ElemType *)realloc(L.elem ,(L.listsize +LISTINCREMENT)*sizeof(ElemType));
free(): 原型:void free (void* list);
C语言中,free可以释放calloc, malloc, realloc动态分配的空间,注意:释放的不是自己定义的指针,而是定义的指针指向的空间。自己定义的普通指针能不能可以通过free
释放,这个要看情况。如果定义的指针指向动态分配的地址空间,则可以使用free释放指针指向的这段空间;否则,就不能使用free释放指针指向的空间。