Skip to content

线性表

约 461 个字 187 行代码 预计阅读时间 5 分钟

Sequential Linear List 顺序存储线性表(数组)

1
2
3
4
5
typedef struct{
    int *data;    //指示动态分配数组的指针
    int MaxSize;  //顺序表最大容量
    int length;   //顺序表当前容量
} SeqList;
  1. 随机存取,即在 \(O(1)\) 时间内找到第 i 个元素
  2. 存储密度(存储利用率)高,每个节点只存储数据元素
  3. 拓展容量不方便
  4. 插入和删除不方便,需要移动大量元素
  5. 元素的逻辑顺序与物理顺序相同

Linked List 链表的创建与添加节点

单向链表通常有个 dummy head,因此头指针存放的不是头结点的内容

typedef struct Node *PtrToNode;
struct Node {
    int element
    PtrToNode next;
};
PtrToNode a = {0,NULL};
PtrToNode newnode = (PtrToNode)malloc(sizeof(struct Node));
newnode->element = 1;
newnode->next = NULL;
a->next = newnode;

Stack 栈

1
2
3
4
5
6
7
struct stackrecord{
    int maxsize;
    int top;
    int *array;
};
struct stackrecord stack={Max,0};
stack.array = (int *)calloc(stack.maxsize+1,sizeof(int));

以链表的方式实现栈

struct Node {
    int data;
    struct Node* next;
};

struct Stack {
    int size;
    struct Node* top;
};

struct Stack* createStack() {
    struct Stack* stack = (struct Stack*) malloc(sizeof(struct Stack));
    stack->size = 0;
    stack->top = NULL;
    return stack;
}

void push(struct Stack* stack, int data) {
    struct Node* newNode = (struct Node*) malloc(sizeof(struct Node));
    newNode->data = data;
    newNode->next = stack->top;
    stack->top = newNode;
    stack->size++;
}

int pop(struct Stack* stack) {
    if (stack->size == 0) {
        return -1;
    }
    struct Node* temp = stack->top;
    int data = temp->data;
    stack->top = temp->next;
    free(temp); // temp 的作用就是释放空间
    stack->size--;
    return data;
}
struct Stack* stack = createStack();

顺序栈

指在物理内存上是有序的,元素值的大小不一定有序

Queue 队列

struct Node {
    int data;
    struct Node* next;
};

struct Queue {
    struct Node *front, *rear;
};

struct Node* newNode(int data) {
    struct Node* temp = (struct Node*)malloc(sizeof(struct Node));
    temp->data = data;
    temp->next = NULL;
    return temp;
}

struct Queue* createQueue() {
    struct Queue* q = (struct Queue*)malloc(sizeof(struct Queue));
    q->front = q->rear = NULL;
    return q;
}

void enQueue(struct Queue* q, int data) {
    struct Node* temp = newNode(data);
    if (q->rear == NULL) {
        q->front = q->rear = temp;
        return;
    }
    q->rear->next = temp;
    q->rear = temp;
}

int deQueue(struct Queue* q) {
    if (q->front == NULL)
        return -1;
    struct Node* temp = q->front;
    int data = temp->data;
    q->front = q->front->next;
    if (q->front == NULL)
        q->rear = NULL;
    free(temp);
    return data;
}

Priority Queue 堆

typedef struct HeapStruct *PriorityQueue; //设为指针形式方便直接在函数中修改值
struct HeapStruct {
    int *Elements;
    int Capacity;
    int Size;
};
// 其实更多以数组的方式实现 左子树:2*index 右子树 2*index+1 父节点 index/2
// 注意,有个dummy head,根节点从index = 1开始

PriorityQueue Initialize(int MaxElements){
    PriorityQueue H=(PriorityQueue)malloc(sizeof(struct HeapStruct));
    if(H==NULL)
        exit(1);
    H->Elements=(int*)malloc((MaxElements+1)*sizeof(int)); 
    //留一个空位给dummyhead
    if(H->Elements==NULL)
        exit(1);
    H->Capacity=MaxElements;
    H->Size=0;
    return H;
}

Delete操作

//进行delete操作,思路是删除最小元素,将队尾元素移至root处,再下滤至符合要求
int DeleteMin(Priority H){
    int i,child;
    int MinElement,LastElement;
    if(isEmpty(H))
        return;
    MinElement = H->Elements[1];
    LastElement = H->Elements[H->Size--];
    for(i=1;i*2<=H->Size;i=child){
        child = i * 2;
        if(child != H->Size && H->Elements[child+1] < H->Elements[child])
            child++;
        if(LastElement > H->Elements[child])
            H->Elements[i] = H->Elements[child];
        else
            break;
    }
    H->Elements[i] = LastElement;
    return MinElement;
}

如果你已经定义过了 PercolateDown 的函数,你也可以直接使用来完成上述 DeleteMin 操作。

void PercolateDown(int p,PriorityQueue H){ 
//下滤操作的时间复杂度为树的高度O(h),minheap情况下大的往下
    int min;
    int value = H->Elements[p];
    while(2*p <= H->Size){
        min = 2*p;
        if(min+1 <= H->Size && H->Elements[min] > H->Elements[min+1])
        //跟子树中较小的比
            ++min;
        if(H->Elements[min] < value){
            H->Elements[p] = H->Elements[min];
            p = min;
        }else
            break;
    }H->Elements[p] = value;
}

void PercolateUp(int p,PriorityQueue H){
    int tmp;
    while(p/2 >= 1 && H->Elements[p/2] > H->Elements[p]){
        tmp = H->Elements[p/2];
        H->Elements[p/2] = H->Elements[p];
        H->Elements[p] = tmp;
        p = p/2;
    }
}

PercolateDown 可以简化删除步骤如下:

H->Elements[1] = H->Elements[H->Size--];
PercolateDown(1,H);

插入操作

将待插入元素放到最后,然后 PercolateUp 即可

1
2
3
4
5
6
7
8
9
void Insert(int X,Priority H){
    int i;
    if(isFull(H)){
        return;
    }
    for(i=++H->Size;H->Elements[i/2]>X;i/=2){ //minHeap的情况
        H->Elements[i]=H->Elements[i/2];
    }H->Elements[i]=X;
}

建堆操作 (调整建堆法)

对于未成堆的Heap,我们一般从\(\lfloor \frac{n}{2} \rfloor\)开始下滤,完成排序,并且时间复杂度为O(n)。

\[T=\sum_1^{h+1} 2^{h+1} -1 -2^h= 2^{h+1} -1-(h+1)=O(n)\]
for(int i=H->Size/2;i>=1;--i)
    PercolateDown(i,H);

d-heap

注意,d-heap 指一个节点有 d 个孩子(仍然是完全树)

1
2
3
4
5
child(i,j) = d*(i-1)+2+j;    
// i是父亲结点的下标,j是第几个儿子,范围是0~d-1
parent(i) = (i-2+d)/d;
last_have_leaf = (size()+1)/d;
// 这是d-heap最后一个有叶子的结点。在heap里是size()/2)

堆排序

利用最大堆最大的元素在根节点的性质,可以对数组进行排序。

具体实现原理为:

  • 先将初始的 R[1,...,n] 建立成最大堆,此时是无序堆,而堆顶是最大元素。
  • 再将堆顶 R[1] 和最后一个元素 R[n] 交换,由此得到新的无序区 R[1,...,n-1] 与有序区 R[n]。
  • 将无序区再次建堆(只用对 R[1] 进行下滤操作即可),重复上述操作。
  • 直到无序区只剩下最后一个元素,可以得到递增序列。
Comments: