当前位置 博文首页 > CrazyCatJack:《数据结构与算法分析》学习笔记-第三章-表、栈和
Hi, all!我自己实现了一个双向循环链表,发布在Github上。
叫QuickList,包含完整的链表模块源码和测试用例。遵循GPL V2.0协议。
大家可以去github上获取,如果觉得好用请帮我点个star,谢谢啦嘿嘿~
QuickList传送门
程序设计的基本法则之一是例程不应超过一页,可以通过把程序模块化实现。每个模块是一个逻辑单位并执行某个特定的任务,它通过调用其他模块而使本身保持很小。优点如下:
概念:抽象数据类型ADT是一些操作的集合。这种操作的实现只在程序中编写一次,而程序中任何其他部分需要在该ADT上运行起哄的一种操作时,可以通过调用合适的函数来进行。如果要改变操作的细节,那么只需要修改运行这些ADT操作的例程就很容易实现。
连续存储,访问很快,插入删除很慢,而且需要定义数组的大小,而这个大小通常要设的大一些浪费内存空间。所以简单数组一般不用来实现表这种结构
指针变量就是包含存储另外某个数据地址的变量。因此,如果P被声明为指向一个结构的指针,那么存储在P中的值就被解释为主存中的一个位置,在该位置能够找到一个结构,该结构的一个域可以通过P->FieldNme访问。使用链表实现表,可以在线性时间内完成PrintList/Find(L, Key)。FindKth(L, i)需要花费O(i)时间。实践中这个界是保守的,因为调用FindKth常常以按i排序的方式进行。例如FindKth(L,2), FindKth(L,3)可以通过对表的一次扫描同时实现。删除命令可以通过一次free操作和一次指针的修改实现;插入命令可以通过一次malloc操作和两次指针修改实现
使用HEAD节点实现一些在表的前面实现插入和删除操作。实现FindPrevious例程,来实现删除节点的操作。
// 书上例程
int
IsEmpty(List L)
{
return L->next == NULL;
}
// 书上例程
int
IsLast(Position P, List L)
{
return P->next == NULL;
}
// 书上例程
Position
Find(Element X, List L)
{
Position P;
P = L->next;
while (P != NULL && P->Element != X) {
P = P->next;
}
return P;
}
// 书上例程
void
Delete(Element X, List L)
{
Position P, TmpCell;
P = FindPrevious(X, L);
if (!IsLast(P, L)) {
TmpCell = P->next;
P->next = TmpCell->next;
free(TmpCell);
}
}
// 书上例程
Position
FindPrevious(Element X, List L)
{
Position P;
P = L;
while (P->Next != NULL && P->next->Element != X) {
P = P->next;
}
return P;
}
// 书上例程
void
Insert(Element X, List L, Postion P)
{
Position new = malloc(sizeof(Position));
if (new == NULL) {
printf("malloc failed!\n");
}
memset(new, 0, sizeof(Position));
new->Element = X;
new->next = P->next;
P->next = new;
}
ADT中还有其他函数,书上未实现,这里我实现出来分享给大家。
List
MakeEmpty(List L)
{
Position P, tmp;
P = L;
while (P->next != NULL) {
tmp = P->next;
P->next = tmp->next;
free(tmp);
}
return L;
}
void
DeleteList(List L)
{
MakeEmpty(L);
free(L);
}
Position
Header(List L)
{
return L;
}
Position
First(List L)
{
return L->next;
}
ElementType
Retrieve(Position P)
{
return P->Element;
}
需要在每个节点上增加一个指向前驱节点的指针,增加了空间的需求,使得插入和删除的空间开销增加一倍,但是删除速度加快了,因为前驱节点无需遍历寻找
最后一个节点的next指针指向第一个节点,如果有头节点则指向头节点。如果是双向链表,则第一个节点的previous指针指向最后的节点
多项式ADT
//书上例程
typedef struct {
int CoeffArray[MaxDegree + 1];
int HighPower;
}
//书上例程
void
ZeroPolynomial(Polynomial Poly)
{
int i;
for (i = 0; i <= MaxDegree; i++) {
Poly.CoeffArray[i] = 0;
}
Poly.HighPower = 0;
}
//书上例程,时间复杂度O(N)
void
AddPolynomial(const Polynomial Poly1, const Polynomial Poly2, Polynomial PolySum)
{
int i;
ZeroPolynomial(PolySum);
PolySum->HighPower = Max(Poly1->HighPower, Poly2->HighPower);
for (i = 0; i < PolySum->HighPower; i++) {
PolySum->CoeffArray[i] = Poly1->CoeffArray[i] + Poly2->CoeffArray[i];
}
}
//书上例程,时间复杂度O(N^2)
void
MultPolynomial(const Polynomial Poly1, const Polynomial Poly2, Polynomial PolyProd)
{
int i, j;
ZeroPolynomial(PolyProd);
PolyProd->HighPower = Poly1->HighPower + Poly2->HighPower;
if (PolyProd->HighPower > MaxDegree) {
printf("Exceeded array size");
} else {
for (i = 0; i < Poly1->HighPower; i++) {
for (j = 0; j < Poly2->HighPower; j++) {
PolyProd->CoeffArray[i + j] += Poly1->CoeffArray[i] * Poly2->CoeffArray[j];
}
}
}
}
另一种方法:单链表。多项式的每一项含在一个节点中,并且这些节点以次数递减的顺序排序。
//书上例程
struct Node {
int Cofficient;
int Exponent;
PtrToNode Next;
}
typedef struct Node *PtrToNode;
typedef PtrToNode Polynomial;
//自己写的
Polynomial
CreatePolynomial()
{
Polynomial tmp;
tmp = (Polynomial)malloc(sizeof(struct Node));
memset(tmp, 0, sizeof(struct Node));
return tmp;
}
//自己写的
void
DestroyPolynomial(Polynomial P)
{
free(P);
}
//自己写的。时间复杂度O(N^2)
void
AddPolynomial(const Polynomial Poly1, const Polynomial Poly2, Polynomial PolySum)
{
int i, j;
Polynomial P1, P2, Psum, Ptmp;
Psum = PolySum;
for (P1 = Poly1->next; P1 != NULL; P1 = P1->next) {
for (P2 = Poly2->next; P2 != NULL; P2 = P2->next) {
if (P1->Exponent == P2->Exponent) {
break;
}
}
Ptmp = CreatePolynomial();
Ptmp->Exponent = P1->Exponent;
if (P2 == NULL) {
Ptmp->Cofficient = P1->Cofficient;
} else {
Ptmp->Cofficient = P1->Cofficient + P2->Cofficient;
}
Psum->next = Ptmp;
Ptmp->next = NULL;
Psum = Psum->next;
}
}
//自己写的。时间复杂度O(N^3)
void
MultPolynomial(const Polynomial Poly1, const Polynomial Poly2, Polynomial PolyProd)
{
int i, j, exponentTmp;
Polynomial P1, P2, Prod, Ptmp;
Prod = PolyProd->next;
for (P1 = Poly1->next; P1 != NULL; P1 = P1->next) {
for (P2 = Poly2->next; P2 != NULL; P2 = P2->next) {
exponentTmp = P1->Exponent + P2->Exponent;
while (Prod != NULL) {
if (Prod->Exponent == exponentTmp) {
Prod->Cofficient += P1->Cofficient * P2->Cofficient;
break;
}
Prod = Prod->next;
}
if (Prod == NULL) {
Ptmp = CreatePolynomial();
Ptmp->Exponent = exponentTmp;
Ptmp->Cofficient = P1->Cofficient * P2->Cofficient;
Prod = PolyProd->next;
PolyProd->next = Ptmp;
Ptmp->next = Prod;
}
}
}
}
桶式排序
// 自己写的,时间复杂度O(N)
void
sortBucket(int *array, int num, int maxSize)
{
int CountArraySize = maxSize + 1;
int *Count = (int *)malloc(CountArraySize * sizeof(int));
memset(Count, 0, CountArraySize * sizeof(int));
int inputCnt;
for (inputCnt = 0; inputCnt < num; inputCnt++) {
Count[array[inputCnt]] = array[inputCnt];
}
for (inputCnt = 1; inputCnt < CountArraySize; inputCnt++) {
if (Count[inputCnt] != 0) {
printf("%d", Count[inputCnt]);
}
}
}
基数排序,我自己实现了并上传到了Github上,大家可以获得源码。
bucketSort2
// SPDX-License-Identifier: GPL-2.0-only
/*
* bucketSort2.c
*
* Copyright (C) 2020 xuri
*/
/*
* This file show how bucket sort running, it based on QuickList module
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "QuickList.h"
#define ARRAY_NUM 10
#define BUCKET_NUM 10 //10 buckets
#define NUMBER_RANGE 3 // 0 - 999
void bucketSort2(int *inputArray, int num)
{
int divisor = 1;
int cnt;
int *recvdata;
int times = 0;
QuickListNode_T *tmp = NULL, *tmp2 = NULL;
QuickListNode_T *qlArray[BUCKET_NUM];
/* initialize the qlArray */
memset(qlArray, 0, BUCKET_NUM);
for (cnt = 0; cnt < BUCKET_NUM; cnt++) {
qlArray[cnt] = QuickListCreateList();
}
/* first time input array and create listnode add to qlArray */
for (cnt = 0; cnt < num; cnt++) {
tmp = QuickListCreateNode(&inputArray[cnt]);
QuickListAddNodetoTail(qlArray[(inputArray[cnt] / divisor % 10)], tmp);
}
printf("after first time\n");
/* finish bucket sort */
while (times < NUMBER_RANGE - 1) {
divisor *= 10;
for (cnt = 0; cnt < BUCKET_NUM; cnt++) {
tmp = NULL;
tmp2 = NULL;
QuickList_for_each_entry_safe(qlArray[cnt], tmp, tmp2) {
recvdata = QuickList_entry(int, tmp);
if ((*recvdata / divisor % 10) != cnt) {
QuickListDetachNode(qlArray[cnt], tmp);
QuickListAddNodetoTail(qlArray[(*recvdata / divisor % 10)], tmp);
}
}
}
times++;
}
/* print array after bucket sort */
printf("Start print array after bucket sort:\n");
for (cnt = 0; cnt < BUCKET_NUM; cnt++) {
QuickList_for_each_entry(qlArray[cnt], tmp) {
recvdata = QuickList_entry(int, tmp);
printf("%d ", *recvdata);
}
}
printf("\n");
}
多重表:节约空间,花费时间。也可以在每个节点中加入链表头指针,花费空间,节约时间。如图:
链表的游标实现
struct node
{
ElementType Element;
Position Next;
}
typedef int PtrToNode;
typedef PtrToNode List;
typedef PtrToNode Position;
struct Node CursorSpace[SpaceSize];
void InitializeCursorSpace(void)
{
int cnt;
for (cnt = 0; cnt < SpaceSize; cnt++) {
CursorSpace[cnt].Element = 0;
if (cnt + 1 >= SpaceSize) {
CursorSpace[cnt].Next = 0;
} else {
CursorSpace[cnt].Next = cnt + 1;
}
}
}
static Position
CursorAlloc()
{
Position P;
P = CursorSpace[0].Next;
CursorSpace[0].Next = CursorSpace[P].Next;
return P;
}
static void
CursorFree(Position X)
{
CursorSpace[X].Next = CursorSpace[0].Next;
CursorSpace[0].Next = X;
}
int
IsEmpty(List L)
{
return (CursorSpace[L].Next == 0);
}
int
IsLast(Position P, List L)
{
return (CursorSpace[P].Next == 0);
}
Position
Find(ElementType X, List L)
{
Position tmp;
for(tmp = CursorSpace[L].Next; tmp != 0; tmp = CursorSpace[tmp].Next) {
if (CursorSpace[tmp].Element == X) {
break;
}
}
return tmp;
}
void
Delete(ElementType X, List L)
{
Position tmp, tmpNext;
for (tmp = L; CursorSpace[tmp].Next != 0; tmp = CursorSpace[tmp].Next) {
tmpNext = CursorSpace[tmp].Next;
if (CursorSpace[tmpNext].Element == X) {
CursorSpace[tmp].Next = CursorSpace[tmpNext].Next;
CursorFree(tmpNext);
}
}
}
void
Insert(ElementType X, List L)
{
Position P;
P = CursorAlloc();
if (P == 0) {
printf("run out of memory\n");
return;
}
CursorSpace[P].Element = X;
CursorSpace[P].Next = CursorSpace[L].Next;
CursorSpace[L].Next = P;
}
节点定义
struct node;
typedef struct node *PtrToNode;
typede PtrToNode Stack;
struct node {
ElementType Element;
PtrToNode Next;
}
Stack
CreateStack(void)
{
Stack s = NULL;
s = (Stack)malloc(sizeof(struct node));
if (s == NULL) {
printf("stack malloc failed\n");
return s;
}
memset(s, 0, sizeof(struct node));
s->Next = NULL;
return s;
}
void
MakeEmpty(Stack s)
{
PtrToNode p = NULL, tmp = NULL;
p = s->Next;
while (p != NULL) {
tmp = p->Next;
free(p);
p = tmp;
}
}
int
Push(ElementType X, Stack s)
{
PtrToNode tmp = NULL;
tmp = (PtrToNode)malloc(sizeof(struct node));
if (tmp == NULL) {
return -1;
}
memset(tmp, 0, sizeof(struct node));
tmp->Element = X;
tmp->Next = s->Next;
s->Next = tmp;
return 0;
}
void
Pop(Stack s)
{
if (s == NULL || s->Next == NULL) {
return NULL;
}
PtrToNode tmp = NULL;
tmp = s->Next;
s->Next = tmp->Next;
free(tmp);
}
ElementType
Top(Stack s)
{
return s->Next.Element;
}
int
IsEmpty(Stack s)
{
if (s == NULL) {
return -1;
}
return (s->Next == NULL);
}
一个影响栈的执行效率的问题是错误检测。除非在错误处理极其重要的场合(如在操作系统中),惯用手法是在栈例程中省去错误检测。当编写程序时,忽略错误检测一般是不妥的,应该随时编写错误检测的代码,如果它们冗长,当它们确实耗费太多时间时,可以将它们去掉。
struct StackRecord;
typedef struct StackRecord *Stack;
#define EmptyTOS (-1)
#define MinStackSize (5)
struct StackRecord
{
int Capacity;
int TopOfStack;
ElementType *Array;
}
Stack
CreateStack(int MaxElement)
{
if (MaxElement < MinStackSize) {
return NULL;
}
Stack s = NULL;
s = (Stack)malloc(sizeof(struct StackRecord));
if (s == NULL) {
return NULL;
}
memset(s, 0, sizeof(struct StackRecord));
s->Capacity = MaxElement;
s->TopOfStack = EmptyTOS;
s->Array = (ElementType *)malloc(sizeof(ElementType) * MaxElement);
if (s->Array == NULL) {
free(s);
return NULL;
}
memset(s->Array, 0, sizeof(ElementType) * MaxElement);
return s;
}
void
DisposeStack(Stack s)
{
if (s->Array != NULL) {
free(s->Array);
s->Array = NULL;
}
if (s != NULL) {
free(s);
}
}
int
IsEmpty(Stack s)
{
return (s->TopOfStack == EmptyTOS);
}
void
MakeEmtpy(Stack s)
{
memset(s->Array, 0, sizeof(ElementType) * s->Capacity);
s->TopOfStack = EmptyTOS;
}
int
IsFull(Stack s)
{
return (s->TopOfStack + 1 == s->Capacity);
}
void
Push(Stack s, ElementType X)
{
if (IsFull(s)) {
printf("stack s is full\n");
return;
}
s->array[++s->TopOfStack] = X;
}
ElementType
TopAndPop(Stack s)
{
if (s->TopOfStack == EmptyTOS) {
return EmptyTOS;
}
return s->array[s->TopOfStack--];
}
void
Pop(Stack s)
{
if (IsEmpty(s)) {
printf("stack s is empty\n");
}
s->TopOfStack--;
}
ElementType
Top(Stack s)
{
return s->Array[s->TopOfStack];
}
平衡符号
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "stackADT.h"
#define BRACKET_TYPE 3
#define TEST_STR "(abcd}[ef])"
char bracketOpen[BRACKET_TYPE] = {'{', '(', '['};
char bracketClose[BRACKET_TYPE] = {'}', ')', ']'};
int symbolCheck(char *inputStr)
{
int strLen = strlen(inputStr);
int cnt, cnt2;
char tmp;
int ret = -1;
Stack s = CreateStack();
if (s == NULL) {
return ret;
}
for (cnt = 0; cnt < strLen; cnt++) {
for (cnt2 = 0; cnt2 < BRACKET_TYPE; cnt2++) {
if (inputStr[cnt] == bracketOpen[cnt2]) {
Push(inputStr[cnt], s);
}
if (inputStr[cnt] == bracketClose[cnt2]) {
tmp = Top(s);
if (tmp != bracketOpen[cnt2]) {
goto __exit;
}
Pop(s);
}
}
}
if (!IsEmpty(s)) {
goto __exit;
}
ret = 0;
__exit:
DistroyStack(s);
return ret;
}
void main()
{
char *p = TEST_STR;
if (0 == symbolCheck(p)) {
printf("check success\n");
} else {
printf("check fail\n");
}
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "stackADT.h"
#define OPERATOR_TYPE 4
char Operator[OPERATOR_TYPE] = {'+', '-', '*', '/'};
char *Expression = "12+3*85-/";
void suffixExpression(char *inputStr)
{
int cnt, cnt2;
int operateNum1, operateNum2;
Stack s = CreateStack();
for (cnt = 0; inputStr[cnt] != '\0'; cnt++) {
if ((inputStr[cnt] >= '0') && (inputStr[cnt] <= '9')) {
printf("Push %d\n", (inputStr[cnt] - '0'));
Push((inputStr[cnt] - '0'), s);
}
for (cnt2 = 0; cnt2 < OPERATOR_TYPE; cnt2++) {
if (inputStr[cnt] == Operator[cnt2]) {
operateNum2 = Top(s);
Pop(s);
operateNum1 = Top(s);
Pop(s);
printf("operator=%c, num1=%d, num2=%d\n", Operator[cnt2], operateNum1, operateNum2);
switch (cnt2) {
case 0: {
Push((operateNum1 + operateNum2), s);
printf("Push %d\n", operateNum1 + operateNum2);
break;
}
case 1: {
Push((operateNum1 - operateNum2), s);
printf("Push %d\n", operateNum1 - operateNum2);
break;
}
case 2: {
Push((operateNum1 * operateNum2), s);
printf("Push %d\n", operateNum1 * operateNum2);
break;
}
case 3: {
Push((operateNum1 / operateNum2), s);
printf("Push %d\n", operateNum1 / operateNum2);
break;
}
default:
break;
}
}
}
}
operateNum1 = Top(s);
Pop(s);
DistroyStack(s);
printf("result=%d\n", operateNum1);
}
void main()
{
suffixExpression(Expression);
}
#define CAPACITY 10
struct QueueRecord {
int Capacity;
int Front;
int Rear;
int Size;
ElementType *Array;
}
typedef struct QueueRecord *Queue;
Queue
CreateQueue(int MaxElements)
{
if (MaxElements <= 0) {
return NULL;
}
Queue q = NULL;
q = (Queue)malloc(sizeofstruct QueueRecord(struct QueueRecord));
if (q == NULL) {
printf("queue malloc failed\n");
return NULL;
}
memset(q, 0, sizeof(struct QueueRecord));
q->Capacity = CAPACITY;
q->Front = 1;
q->Rear = 0;
q->Size = 0;
q->Array = (ElementType *)malloc(sizeof(ElementType) * CAPACITY);
if (q->Array == NULL) {
return NULL;
}
memset(q->Array, 0, sizeof(ElementType) * CAPACITY);
return q;
}
void
DisposeQueue(Queue Q)
{
if (Q == NULL) {
return;
}
if (Q->Array != NULL) {
free(Q->Array);
}
free(Q);
}
int
IsEmpty(Queue Q)
{
if (Q == NULL) {
return -1;
}
return (Q->Size == 0);
}
int
IsFull(Queue Q)
{
if (Q == NULL) {
return -1;
}
return (Q->Size == Q->Capicity);
}
int
Enqueue(Queue Q, Element X)
{
if (Q == NULL) {
return -1;
}
if (IsFull(Q)) {
printf("Queue Q is full\n");
return -1;
}
if (++(Q->Rear) >= Q->Capicity) {
Q->Rear -= Q->Capicity;
}
Q->Array[Q->Rear] = X;
Q->Size++;
return 0;
}
int
Dequeue(Queue Q)
{
if (Q == NULL) {
return -1;
}
if (IsEmpty(Q)) {
printf("Queue Q is empty\n");
return -1;
}
if (++(Q->Front) >= Q->Capicity) {
Q->Front -= Q->Capicity;
}
Q->Size--;
return 0;
}
ElementType
FrontAndDequeue(Queue Q)
{
if (Q == NULL) {
return -1;
}
if (IsEmpty(Q)) {
printf("Queue Q is empty\n");
return -1;
}
ElementType ret = Q->Array[Q->Front];
if (++(Q->Front) >= Q->Capicity) {
Q->Front -= Q->Capicity;
}
Q->Size--;
return ret;
}
typedef struct Queue {
ElementType Element;
struct Queue *Pre;
struct Queue *Next;
} Queue_T;
typedef Queue_T *QueuePtr
typedef QueuePtr QueueHead
#define MaxSize 10
#define EMPTY -1
int
IsEmpty(QueueHead Q)
{
if (Q == NULL) {
return -1;
}
return (Q->Next == Q);
}
int
IsFull(QueueHead Q)
{
if (Q == NULL) {
return -1;
}
int queueCnt = 0;
QueuePtr tmp = NULL;
tmp = Q->Next;
while (tmp != Q) {
queueCnt++;
tmp = tmp->Next;
}
return (queueCnt == MaxSize);
}
QueueHead
CreateQueue()
{
QueueHead Q = NULL;
Q = (QueuePtr)malloc(sizeof(struct Queue));
if (Q == NULL) {
return NULL;
}
memset(Q, 0, sizeof(struct Queue));
Q->Next = Q;
Q->Pre = Q;
return Q;
}
int
DisposeQueue(QueueHead Q)
{
if (Q == NULL) {
return -1;
}
MakeEmpty(Q);
free(Q);
return 0;
}
int
MakeEmpty(QueueHead Q)
{
if (Q == NULL) {
return -1;
}
QueueHead tmp = NULL, tmpNext = NULL;
tmp = Q->Next;
while (tmp != Q) {
if (tmp) {
tmpNext = tmp->Next;
free(tmp);
tmp = tmpNext;
}
}
return 0;
}
int
Enqueue(QueueHead Q, ElementType Element)
{
if (Q == NULL) {
return -1;
}
if (IsFull(Q)) {
return -1;
}
QueuePtr qAdd = NULL;
qAdd = (QueuePtr)malloc(sizeof(struct Queue));
if (qAdd == NULL) {
return -1;
}
memset(qAdd, 0, sizeof(struct Queue));
qAdd->Element = Element;
qAdd->Pre = Q->Pre;
qAdd->Next = Q;
Q->Pre->Next = qAdd;
Q->Pre = qAdd;
return 0;
}
ElementType
Front(QueueHead Q)
{
if (Q == NULL) {
return EMPTY;
}
if (IsEmpty(Q)) {
return EMPTY;
}
return Q->Next->Element;
}
int
Dequeue(QueueHead Q)
{
if (Q == NULL) {
return -1;
}
if (IsEmpty(Q)) {
return -1;
}
QueuePtr tmp = NULL;
tmp = Q->Next;
Q->Next = tmp->Next;
tmp->Next->Pre = Q;
if (tmp != NULL) {
free(tmp);
}
return 0;
}
ElementType
FrontAndDequeue(QueueHead Q)
{
if (Q == NULL) {
return EMPTY;
}
if (IsEmpty(Q)) {
return EMPTY;
}
ElementType ret;
QueuePtr tmp = NULL;
tmp = Q->Next;
ret = tmp->Element;
Q->Next = tmp->Next;
tmp->Next->Pre = Q;
if (tmp != NULL) {
free(tmp);
}
return ret;
}
排队论:问题取决于用户加入队列的频率,以及一旦用户得到服务时处理服务的时间。
递归非常强大,但它并不是完全随意的操作;递归的误用和乱用可能导致程序崩溃。
本文作者: CrazyCatJack
本文链接: https://www.cnblogs.com/CrazyCatJack/p/13321878.html
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
关注博主:如果您觉得该文章对您有帮助,可以点击文章右下角推荐一下,您的支持将成为我最大的动力!