数据结构(一)


1.引入

计算机 = 硬件 + 软件(程序)

程序 = 数据结构 + 算法

数据(data):是对客观事物的符号表示。在计算机科学中是指所有能被输入到计算机中并且能被计算机程序处理的符号的总称

数据元素(data element):数据元素是数据的基本单元;在计算机中通常作为一个整体进行考虑和处理。

数据项(data item):一个数据元素由若干个数据项组成,数据项是数据不可分割的最小单位

数据对象(data object):是性质相同的元素的集合,数数据的一个子集

数据结构不仅要保存数据(D),也要保存数据与数据之间的关系(结构 (S))

结构(structure):数据元素之间关系的不同特性

根据数据元素之间的关系,通常由一下4类结构

  • 集合:
  • 线性结构
  • 树状结构
  • 网状结构

数据结构定义形式为:数据结构是一个二元组

\[Data\_Structure = \{D,S\} \]

其中:

  • D是数据元素的有限集
  • S是D上关系的有限集

结构定义之间的关系描述的数据元素之间的逻辑关系,因此成为数据的逻辑结构

数据结构在计算机中的表示方式成为物理结构,又称存储结构

2.线性表

abcdefghijklmnopqrstuvwxyz

a是第一个,a在b的前面,b在a的后面....

线性表中的数据元素可以是各种各样的,但是同一个线性表中必定具有相同的特性,即属于同一数据对象,相邻的元素之间存在这序偶关系。若将线性表记为

\[(a_1, a_2, a_3, ..., a_n) \]

  • 存在着唯一一个被称为第一个的数据元素
  • 存在着唯一一个被称为最后一个的数据元素
  • 除了第一个外,集合中的每一个元素有且仅有一个前驱数据元素
  • 除了最后一个外,集合中的每一个元素有且仅有一个后继数据元素

线性表的物理结构的实现:

  1. 顺序结构
    • 线性表的顺序结构表示的是用一组地址连续的存储单元依次存储线性表中的数据元素。
    • 在这种情况下,我们既保存数据元素,也使用物理地址体现了数据元素之间的逻辑关系
    • 逻辑关系 与 物理地址关系 一致
    • 顺序结构的特点:访问方便、不够灵活
  2. 链式结构
    • 保存每个数据元素时,不必使用连续的地址空间。
    • 数据元素的存储单元是不连续的,但是在保存每个数据元素的同时,需要开辟额外的空间(指针)来记录它逻辑上的上一个或者下一个元素。

2.1单链表

只有一个方向(上一个或者下一个)的链表

typedef int E;

typedef struct linkList
{
    E element;				//数据域,保存元素
    struct linkList *next;	//指针域,保存指向下一个节点的指针
} linkListDef;

新建一个头节点,用来指向第一个节点

linkListDef head;
head.next = NULL;

创建一个节点插入到链表中

void insertLinkList(linkListDef *head, E element, int index)
{
    if(index < 1) return;
    linkListDef *newNode;
    linkListDef *p = head;
    newNode = malloc(sizeof(linkListDef));
    newNode->element = element;
    newNode->next = NULL;
    if(head->next == NULL)
    {
        head->next = newNode;
    }
    else
    {
        while (--index)
        {
            p = p->next;
        }
        newNode->next = p->next;
        p->next = newNode;
    }
}

打印链表中的元素

void printLinkList(linkListDef *head)
{
    linkListDef *p = head->next;
    while (p)
    {
        printf("%d ", p->element);
        p = p->next;
    }
    printf("\n");
}

从链表中删除某个元素

void delNodeForElement(linkListDef *head, E element)
{
    linkListDef *p = head;
    linkListDef *pre = p;
    while (p)
    {
        if(p->element == element)
        {
            pre->next = p->next;
            free(p);
            p = pre->next;
        }
        else
        {
            pre = p;
            p = p->next;
        }
    }  
}

释放链表

void freeLinkList(linkListDef *head)
{
    linkListDef *p = head->next;
    linkListDef *temp = p;
    while (p)
    {
        temp = p->next;
        free(p);
        p = temp;
    }
    head->next = NULL;
}

2.2带管理节点的单链表

typedef int E;

typedef struct linkedList
{
    E element;
    struct linkedList *next;
} linkedListDef;


typedef struct manageLinked
{
    E listNum;
    linkedListDef *first;
    linkedListDef *last;
} manageLinkedDef;

初始化链表

void linkedListInit(manageLinkedDef *list)
{
    list->first = NULL;
    list->last = NULL;
    list->listNum = 0;
}

创建一个节点

linkedListDef *createLinkedListNode(E element)
{
    linkedListDef *newNode;
    newNode = (linkedListDef *)malloc(sizeof(linkedListDef));
    newNode->element = element;
    newNode->next = NULL;
    return newNode;
}

将一个节点从尾部插入链表

void addNodeToManageLinkedList(manageLinkedDef *list, E element)
{
    linkedListDef *newNode = createLinkedListNode(element);
    linkedListDef *p = list->first;
    if(list->first == NULL)
    {
        list->first = newNode;
    }
    else
    {
        while (p->next)
        {
            p = p->next;
        }
        p->next = newNode;
        list->last = newNode;
    }
    list->listNum++;
}

将节点从指定位置插入链表

void insertNodeToManageLinkedList(manageLinkedDef *list, E element, int index)
{
    if(index < 1) return;
    linkedListDef *newNode = createLinkedListNode(element);
    linkedListDef *p = list->first;
    linkedListDef *pre = p;
    if(list->first == NULL)
    {
        list->first = newNode;
    }
    else
    {
        if(index == 1)
        {
            newNode->next = list->first;
            list->first = newNode;
        }
        else
        {
            index--;
            while (--index)
            {
                pre = p;
                p = p->next;
            }
            newNode->next = p->next;
            pre->next = newNode;
        }
        p = list->first;
        while (p->next)
        {
            p = p->next;
        }
        list->last = p;
    }
    list->listNum++;
}

将某个值的所有节点从链表中删除

void DelElementInLinkedList(manageLinkedDef *list, E element)
{
    linkedListDef *p = list->first;
    linkedListDef *temp = p;
    while (p)
    {
        if(p->element == element)
        {
            temp->next = p->next;
            free(p);
            list->listNum--;
        }
        temp = p;
        p = p->next;
    }
}

删除指定位置的节点

void DelIndexInLinkedList(manageLinkedDef *list, int index)
{
    linkedListDef *p = list->first;
    linkedListDef *temp = NULL;
    if(index == 1)
    {
        temp = list->first;
        free(list->first);
        list->first = list->first->next;
        
    }
    else
    {
        while (--index)
        {
            temp = p;
            p = p->next;
        }
        temp->next = p->next;
        free(p);
    }
    list->listNum--;
}

销毁链表

void freeManageLinkedList(manageLinkedDef *list)
{
    linkedListDef *p = list->first;
    linkedListDef *temp = p;
    while (p)
    {
        temp = p->next;
        free(p);
        p = p->next;
    }
    list->first = NULL;
    list->last = NULL;
    list->listNum = 0;
}

打印链表中的元素

void printLinkedList(manageLinkedDef *list)
{
    linkedListDef *p = list->first;
    while (p)
    {
        printf("%d ", p->element);
        p = p->next;
    }
    printf("\n");
}

2.3顺序表

数组:

  • 缺点:不方便扩展空间,容易浪费空间,插入/删除消耗时间多
  • 优点:访问方便

链表

  • 缺点,范围较为麻烦,空间消耗大
  • 优点:空间浪费比较少,插入/删除消耗时间少

顺序表的一体化结构

typedef int E;
#define SEQLISTLEN 1024

typedef struct seqlist
{
    E len;
    E list[SEQLISTLEN];
} seqListDef;

顺序表的分离式结构

typedef struct seqList
{
    E len;
    E size;
    E *list;
} seqListDef;

对应一个顺序表有以下操作

  • 初始化一个线性表
  • 创建一个线性表
  • 输出线性表
  • 判断表是否为空
  • 增加一个元素
  • 删除一个元素
  • 头插法
  • 尾插法
  • 求长度
  • 按位置查找
  • 按元素查找

2.4时间复杂度和空间复杂度

时间复杂度主要衡量算法的运行速度,空间复杂度主要衡量完成这个算法所需要的额外空间;然而,有时两者不能兼得,那我们就要从中去找一个平衡点(时空权衡)。

时间复杂度:

  • 在计算机科学中,算法的时间复杂度是一个函数,它描述了该算法运行的时间。一个算法执行所消耗的时间理论上是不太好计算的,运行的具体时间不仅仅与算法本身有关,还与计算机的性能有关,所以我们讨论时间复杂度的时候并不是讨论具体的时间消耗
  • 一个算法所花费的时间与其中的语句的执行次数成正比,该算法中基本操作的次数,就是该算法的时间复杂度
  • 时间复杂度一般使用O来表示

空间复杂度:

  • 空间复杂度是对一个算法在运行过程中临时占用的存储空间大小的度量,讨论空间复杂度并不是讨论这个算法占用多少个字节的空间,空间复杂度算的是变量的个数
  • 空间复杂度一般用O来表示

时间复杂度计算的示例

void swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}
//时间复杂度是O(1)

for(int i = 0; i < n; i++)
{
    for(int j = 0; j < m; j++)
        num++;
}
//时间复杂度是O(n*m)

推导O的基本原则

  • 常数用1来代替
  • 如果计算过程中,有很多阶,如O(n2+2n+1),只保留最高阶O(n2)
  • 如果最高阶项存在且不是1,则去除与这个项相乘的常数O(2n) O(n)

空间复杂的计算示例

  • 冒泡排序:O(1)
  • 递归求斐波那契数列的第n项:O(n)

2.5双向链表

typedef int E;
typedef struct DLinkedList
{
    //数据域
    E element;
    //指针域
	struct DLinkedList *next;//指向下一个节点
	struct DLinkedListNode *prev;//指向上一个节点
} DLinkedListDef;

2.6循环链表

2.6.1单向循环链表

2.6.2双向循环链表

2.7通用链表

struct node
{
    void *element;
    struct node *next;
};

2.8内核链表

3.栈和队列

3.1栈(stack)

栈是限定仅在 表尾(栈顶)进行插入和删除操作的线性表。因此,对于栈而言,表尾有其特殊的含义,我们称之为栈顶(top),另一端称之为栈底(bottom)

不含元素的栈称为空栈,从栈顶插入元素称之为 入栈, 从栈顶删除元素称之为 出栈

对于栈中的元素而言,先入栈的后出去,后入栈的先出去 --- 先进后出、后进先出(LIFO)

栈的实现:

栈是一种线性表,实现方式有两种,一种是链式栈,另一种是顺序栈。

栈的基本操作:

  1. 初始化一个栈
  2. 入栈 往栈顶增加一个元素
  3. 出栈 从栈顶删除一个元素
  4. 判断栈是否为空
  5. 获取栈顶元素 -- (不出栈)
  6. 清空一个栈
  7. 销毁一个栈

链式栈:

// 栈结点类型
typedef int StackElemType;
typedef struct stacknode
{
    StackElemType data;
    struct stacknode *next;
}StatckNode;

typedef struct stack
{
    struct stacknode *top;		//指向栈顶
    struct stacknode *bottom;	//指向栈底
    int num;					//保存栈中元素个数
}LnkStack;

initStack();
Push();	//入栈(压栈)
Pop();	//出栈(弹栈)
stackIsEmpty();
getTop();
stackClear();
stackDestory();

/**
*linkedStack.c
*/
#include <stdio.h>
#include <stdlib.h>
#include "linkedStack.h"

linkedStackDef *stackInit()
{
    linkedStackDef *stack = malloc(sizeof(linkedStackDef));
    stack->top = NULL;
    stack->bottom = NULL;
    stack->Num = 0;
    return stack;
}

void Push(linkedStackDef *stack, E element)
{
    stackNodeDef *newNode = malloc(sizeof(stackNodeDef));
    newNode->element = element;
    newNode->next = NULL;
    if(stack->top == NULL)
    {
        stack->top = newNode;
        stack->bottom = newNode;
    }
    else
    {
        newNode->next = stack->top;
        stack->top = newNode;
    }
    stack->Num++;
}

E Pop(linkedStackDef *stack)
{
    stackNodeDef *temp;
    E element;
    if(stack->top == NULL)
    {
        stack->bottom = NULL;
    }
    else
    {
        temp = stack->top;
        stack->top = temp->next;
        element = temp->element;
        free(temp);
        stack->Num--;
        return element;
    }
}

int stackIsEmpty(linkedStackDef *stack)
{
    return stack->Num;
}

stackNodeDef *getTop(linkedStackDef *stack)
{
    return stack->top;
}

void stackClear(linkedStackDef *stack)
{
    while (stack->top)
    {
        Pop(stack);
    }
    
}

void stackDestroy(linkedStackDef *stack)
{
    stackClear(stack);
    free(stack);
}

/**
*linkedStack.h
*/
#ifndef _LINKEDSTACK_H_
#define _LINKEDSTACK_H_

typedef int E;

typedef struct StackNode
{
    E element;
    struct StackNode *next;
} stackNodeDef;

typedef struct LinkedStack
{
    stackNodeDef *top;
    stackNodeDef *bottom;
    int Num;
} linkedStackDef;

linkedStackDef *stackInit();

void Push(linkedStackDef *stack, E element);

E Pop(linkedStackDef *stack);

int stackIsEmpty(linkedStackDef *stack);

stackNodeDef *getTop(linkedStackDef *stack);

void stackClear(linkedStackDef *stack);

void stackDestroy(linkedStackDef *stack);

#endif /*linkedStack.h*/

顺序栈

typedef int E;
typedef struct seqStack
{
    E *element;		//指向一个描述栈的空间
    int lenth;		//栈的容量
    int top;		//栈顶下标
} seqStackDef;

initStack();
Push();	//入栈(压栈)
Pop();	//出栈(弹栈)
stackIsEmpty();
getTop();
stackClear();
stackDestory();

/**
*seqStack.c
*/
#include <stdio.h>
#include <stdlib.h>
#include "seqStack.h"

void seqStackInit(seqStackDef *stack)
{
    stack->element = malloc(sizeof(E)*10);
    stack->lenth = 10;
    stack->top = -1;
}

void Push(seqStackDef *stack, E element)
{
    if(stack->top == stack->lenth-1)
    {
        stack->lenth += sizeof(E) * 10;
        stack->element = realloc(stack->element,stack->lenth);
    }
    stack->top++;
    stack->element[stack->top] = element;
}

E Pop(seqStackDef *stack)
{
    E element = stack->element[stack->top];
    stack->element[stack->top] = 0;
    stack->top--;
    return element;
}

E getTop(seqStackDef *stack)
{
    return stack->element[stack->top];
}

int stackIsEmpty(seqStackDef *stack)
{
    return stack->top+1;
}

void stackClear(seqStackDef *stack)
{
    while (stack->top != -1)
    {
        Pop(stack);
    }  
}

void stackDestroy(seqStackDef *stack)
{
    stackClear(stack);
    free(stack->element);
}

/**
*seqStack.h
*/
#ifndef _SEQSTACK_H_
#define _SEQSTACK_H_

typedef int E;

typedef struct seqStack
{
    E *element;
    int lenth;
    int top;
} seqStackDef;

void seqStackInit(seqStackDef *stack);

void Push(seqStackDef *stack, E element);

E Pop(seqStackDef *stack);

E getTop(seqStackDef *stack);

int stackIsEmpty(seqStackDef *stack);

void stackClear(seqStackDef *stack);

void stackDestroy(seqStackDef *stack);

#endif

3.2后缀表达式

数学计算:a + b * c + (e * d + f) * g 中缀表达式

转化为后缀表达式:abc * + ed * f + g * +

转化规则:

  1. 遇到操作数,直接输出
  2. 遇到左括号则入栈;遇到右括号,则出栈到遇到左括号为止(左括号只有遇到相匹配的右括号才出栈),左括号只出栈不输出
  3. 遇到运算符,从栈中弹出元素,直到遇到更低优先级的元素为止,然后将遇到的运算符入栈
  4. 如果读到了中缀表达式的结尾,则将栈中的元素全部弹出

后缀表达式的计算

  1. 遍历后缀表达式,碰到数字入栈
  2. 读到运算符,出栈两个操作数,得到结果入栈

3.3队列(Queue)

队列是一种先进先出(FIFO)的线性表,操作仅限于队尾(rear)插入,队首(front)删除

链式队列

typedef int E;
typedef struct linkQueueNode
{
    E element;
    struct linkQueueNode *next;
} linkQueueNodeDef;

typedef struct linkQueue
{
    linkQueueNodeDef *front;	//指向队列头部
	linkQueueNodeDef *rear;	//指向队列尾部
    int num;
} linkQueueDef;

//队列的基本操作
linkQueueInit();	//初始化一个队列
void inQueue(linkQueueDef *queue, E element);//入队
E outQueue(linkQueueDef *queue);//出队
linkQueueNodeDef *getFront(linkQueueDef *queue);//获取队首
int linkQueueIsEmpty(linkQueueDef *queue);//判断队首是否为空
void queueClear(linkQueueDef *queue);//清空队列
void queueDestroy(linkQueueDef *queue);//销毁队列

/**
*linkQueue.c
*/
#include "linkQueue.h"
#include <stdio.h>
#include <stdlib.h>

linkQueueDef *linkQueueInit()
{
    linkQueueDef *queue = malloc(sizeof(linkQueueDef));
    queue->front = NULL;
    queue->rear = NULL;
    queue->num = 0;
    return queue;
}

void inQueue(linkQueueDef *queue, E element)
{
    linkQueueNodeDef *newNode = malloc(sizeof(linkQueueNodeDef));
    newNode->element = element;
    newNode->next = NULL;
    if(queue->rear == NULL)
    {
        queue->front = newNode;
        queue->rear = newNode;
    }
    else
    {
        queue->rear->next  = newNode;
        queue->rear = newNode;
    }
    queue->num++;
}

E outQueue(linkQueueDef *queue)
{
    linkQueueNodeDef *p = queue->front;
    E element = queue->front->element;
    queue->front = queue->front->next;
    free(p);
    queue->num--;
    return element;
}


linkQueueNodeDef *getFront(linkQueueDef *queue)
{
    return queue->front;
}

int linkQueueIsEmpty(linkQueueDef *queue)
{
    return queue->num;
}

void queueClear(linkQueueDef *queue)
{
    while (linkQueueIsEmpty(queue))
    {
        outQueue(queue);
    }
}


void queueDestroy(linkQueueDef *queue)
{
    queueClear(queue);
    free(queue);
}

/*
*linkQueue.h
*/
#ifndef _LINKQUEUE_H_
#define _LINKQUEUE_H_

typedef int E;
typedef struct linkQueueNode
{
    E element;
    struct linkQueueNode *next;
} linkQueueNodeDef;

typedef struct linkQueue
{
    linkQueueNodeDef *front;	//指向队列头部
    linkQueueNodeDef *rear;		//指向队列尾部
    int num;
} linkQueueDef;

/// @brief 初始化一个队列
/// @return 队列
linkQueueDef *linkQueueInit();

/// @brief 入队
/// @param queue 队列
/// @param element 元素
void inQueue(linkQueueDef *queue, E element);

/// @brief 出队
/// @param queue 队列
/// @return 元素
E outQueue(linkQueueDef *queue);

/// @brief 获取队首
/// @param queue 队列
/// @return 队列节点
linkQueueNodeDef *getFront(linkQueueDef *queue);

/// @brief 判断队列是否为空
/// @param queue 队列
/// @return 队列长度
int linkQueueIsEmpty(linkQueueDef *queue);

/// @brief 清空队列
/// @param queue 队列
void queueClear(linkQueueDef *queue);

/// @brief 销毁队列
/// @param queue 队列
void queueDestroy(linkQueueDef *queue);

#endif

posted @ 2023-07-26 15:27  乐情在水静气同山  阅读(12)  评论(0编辑  收藏  举报