1.什么是数据结构

指将数据按特定的规则,存储到特定的结构中

2.算法效率

指解决问题的效率。解决某问题的算法的好坏取决于所需内存空间

和花费的时间

而其时间和空间用复杂度来衡量(采用大O的渐进表示法)

时间复杂度:算法运行的快慢》》》》基本操作的执行次数

空间复杂度:算法运行所需的空间》》》运行中额外产生的内存消耗

推导大O阶方法(以时间复杂度为例)

1.用常数1取代运行时间中所需要的加法常数

2.在修改后的运行次数函数中,只保留最高阶项

3.如果最高阶项存在且不是1,则去出与这个项目相乘的常数,得到的结果就是大O阶

实质:去掉对结果影响不大的数

如用二分查找递增数集的某个数

其时间的复杂度为以2为底的log(n)

斐波那契数列的递归算法的时间复杂度为2的n次方

通常情况下,时间复杂度和空间复杂度越接近O(1),其算法就越优。

3.线性表

就是具有线性关系的数据结构

如顺序表,链表,栈,队列....

顺序表就是个数组,可以根据实际情况变形为动态和非动态增长

其结构特点为,数据连续存放,且只能从前往后放,不能越界存储

由于是个数组,顺序表支持随机访问,从空间的角度看,数据是连续存放的

可通过malloc函数或realloc函数实现动态增长,有利于充分利用空间

bool SeqListempty(SeqList*ps)
{return ps->capacity == ps->size;}
void check_capacity(SeqList*ps)
{if (ps->capacity == ps->size)//判断增容{if (ps->capacity == 0){ps->capacity = 4;}else{ps->capacity *= 2;}SLDateType*tmp = (SLDateType*)realloc(ps->a, ps->capacity*sizeof(SLDateType));if (tmp == NULL){printf("增容失败\n");}else{ps->a = tmp;}}
}
void SeqListInit(SeqList* ps)
{assert(ps);ps->a = NULL;ps->size = 0;ps->capacity = 0;
}
void SeqListDestory(SeqList* ps)
{free(ps->a);ps->size = 0;ps->capacity = 0;}
void SeqListPrint(SeqList* ps)
{assert(ps);size_t i = 0;for ( i = 0; i < ps->size; i++){printf("%d ", ps->a[i]);}printf("\n");
}
void SeqListPushBack(SeqList* ps, SLDateType x)
{assert(ps);check_capacity(ps);ps->a[ps->size] = x;ps->size++;
}
void SeqListPushFront(SeqList* ps, SLDateType x)
{assert(ps);check_capacity(ps);size_t i = 0;SLDateType tmp = 0;SLDateType tmp1 = 0;tmp = ps->a[i];ps->a[i] = x;ps->size++;for (i=1; i < (ps->size); i++){tmp1 = tmp;tmp = ps->a[i];ps->a[i] = tmp1;}
}
void SeqListPopFront(SeqList* ps)
{assert(ps&&ps->size);SeqList*tmp = ps;ps->a = ps->a + 1;free(tmp);ps->size--;}
void SeqListPopBack(SeqList* ps)
{assert(ps&&ps->size);ps->size--;
}// 顺序表查找
int SeqListFind(SeqList* ps, SLDateType x)
{assert(ps&&ps->size);size_t i = 0;while (i<ps->size){if (ps->a[i] == x){return i;}}printf("没找到\n");return -1;
}
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, size_t pos, SLDateType x)
{assert(ps);check_capacity(ps);ps->size++;SLDateType tmp = ps->a[pos];SLDateType tmp1 = 0;ps->a[pos] = x;size_t i = pos + 1;while (i<ps->size){tmp1 = ps->a[i];ps->a[i] = tmp;tmp = tmp1;i++;}}
// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, size_t pos)
{assert(ps&&ps->size);check_capacity(ps);size_t i = pos;while (i<ps->size-1){ps->a[i] = ps->a[i + 1];}ps->size--;}

上面对顺序表的增删查改的接口函数实现(老久之前写的,没仔细检查过,可能存在漏洞,懒得看了)

接下来是链表

链表按结构上划分有8种结构

而实际上常用得只有两种

一种是无头单向非循环链表(结构简单,实现有点麻烦)也叫单链表

另一个是带头双向循环链表(结构较上复杂那么点,但实现起来方便)

单链表就是定义一个结构体,里面放则下一个结构体的地址,和所需存储的数据

根据该地址可以找到下一个结点,具体如图

下面是单链表接口函数的实现

// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x)
{SListNode*tmp = (SListNode*)malloc(sizeof(SListNode));if (tmp == NULL){printf("开辟空间失败\n");}else{tmp->data = x;tmp->next = NULL;return tmp;}
}
// 单链表打印
void SListPrint(SListNode* plist)
{assert(plist);while (plist){printf("%d ", plist->data);plist = plist->next;}printf("\n");
}
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{assert(pplist);(*pplist)->next=BuySListNode(x);}
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{assert(pplist);SListNode*tmp = *pplist;(*pplist)=BuySListNode(x);(*pplist)->next = tmp;
}
// 单链表的尾删
void SListPopBack(SListNode** pplist)
{assert(pplist);SListNode *tmp = (*pplist);while ((*pplist)->next != NULL){tmp = (*pplist);(*pplist) = (*pplist)->next;}free(tmp->next);*pplist = NULL;}
// 单链表头删
void SListPopFront(SListNode** pplist)
{assert(pplist);SListNode*tmp = (*pplist)->next;free(*pplist);*pplist = tmp;}
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{assert(plist);while (plist){if (plist->data == x){printf("找到了\n");return plist;}plist=plist->next;}printf("没找到\n");return NULL;
}
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
//就单链表而言,后面位置比前面位置更容易处理
void SListInsertAfter(SListNode* pos, SLTDateType x)
{assert(pos);SListNode*p_tmp = NULL;SListNode*tmp=pos->next;p_tmp = BuySListNode(x);pos->next = p_tmp;p_tmp = tmp;}
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
//如果删除pos位置,会使前一个位置不易找到
void SListEraseAfter(SListNode* pos)
{assert(pos||pos->next);SListNode*tmp=pos->next;if (tmp->next == NULL){free(tmp);pos->next = NULL;}else{pos->next = tmp->next;free(tmp);}
}
// 单链表的销毁
void SListDestory(SListNode* plist)
{while (plist){SListNode*tmp = plist;plist = plist->next;free(tmp);}
}

(写的时间过于久远,没有排查过是否有漏洞,但大体思路应该没问题)

而带头双向循环链表其结构如下(可能是)

画图表示的话是这样的

具体接口函数的代码实现不知道放哪了,就不写出来了吧

接下来是有关链表的几个实际OJ题(只有思路,过程懒得写)这里的链表指的是单链表

1. 删除链表中等于给定值 val 的所有节点

考察的是对链表的数据删除

定义两个一指针A,B,还有个删除指针C

一个指针A在前,一个指针B在后,往后迭代寻找

如果相同,把A所指向的结点付给C,A往后走,B指向A

然后释放C所指向的空间即可

2. 反转一个单链表。

两个思路,一个是在原链表上进行操作

另一个思路是

给一个头指针,利用单链表的头增,将数据一个一个的移下来

3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个
中间结点。

思路:用快慢指针即可解决

4. 输入一个链表,输出该链表中倒数第k个结点。

思路:上一题的快慢指针的变形题

5. 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成
的。

思路:定义3个指针,一个负责形成新链表,另两个用来操作原链表

给两个链表都定义一个指针。按一定规则比较后,拿下来,指针往后走,循环即可

6. 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。

思路:形成两个新链表,一个放小于X的部分。另个放大于X的

最后将其连接即可

链接
7. 链表的回文结构

思路:找到中间结点。将后半部分反转,再与前半部分比较即可

8. 输入两个链表,找出它们的第一个公共结点

思路:算出两个链表的长度

定义两个指针,让一个先走K步,最后同时走,当地址相同时返回即可

9. 给定一个链表,判断链表中是否有环

思路:快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针从链表其实位置开始运行,如果链表
带环则一定会在环中相遇,否则快指针率先走到链表的末尾。(课件上的,我偷懒了)

这个实际上就是个追及问题,在该循环结构中,如果一个慢指针走一步,快指针走两步,则他们之间的距离会一个一个的减少,最终会相遇。

顺序表和链表的区别

存储空间上:顺序表是连续存储,而链表在逻辑上连续。但实际物理储存不一定连续

随机访问:顺序表支持,链表不支持

任意位置删除或插入元素:顺序表可能需要搬动元素,而链表改动指针指向即可

插入:动态顺序表,空间不够时扩容,链表没有容量的概念

——————————————————————————————————————————

接下来是栈

栈的结构特点是先进后出

即数据先进去的最后出来

可以用数组栈或链式栈实现(从效率角度来看,用数组来实现栈更优)

这是栈的有关接口函数的实现

#include"stack.h"// 初始化栈
void StackInit(Stack* ps)
{ps->_a = NULL;ps->_capacity = 0;ps->_top = 0;
}// 入栈
void StackPush(Stack* ps, STDataType data)
{assert(ps);int newcapacity = 0;if (ps->_capacity == ps->_top){if (ps->_capacity == 0){newcapacity = 4;}else{newcapacity = ps->_capacity * 2;}STDataType*tmp = (STDataType*)realloc(ps->_a, newcapacity*sizeof(STDataType));assert(tmp);ps->_a = tmp;ps->_capacity = newcapacity;}ps->_a[ps->_top] = data;ps->_top++;
}// 出栈
void StackPop(Stack* ps)
{assert(ps);if(ps->_top > 0){ps->_top--;}else{printf("栈数据为空,pop失败\n");}
}// 获取栈顶元素
STDataType StackTop(Stack* ps)
{assert(ps);assert(ps->_a);return ps->_a[ps->_top-1];
}// 获取栈中有效元素个数
int StackSize(Stack* ps)
{assert(ps);assert(ps->_a);if (ps->_top == 0){return ps->_top;}else{return ps->_top - 1;}
}// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps)
{assert(ps);return ps->_top == 0;
}// 销毁栈
void StackDestroy(Stack* ps)
{assert(ps);free(ps->_a);ps->_a = NULL;ps->_capacity = 0; ps->_top = 0;
}MyQueue* myQueueCreate() {MyQueue*tmp = (MyQueue*)malloc(sizeof(MyQueue));StackInit(&tmp->s1);StackInit(&tmp->s2);return tmp;
}void myQueuePush(MyQueue* obj, int x) {if (obj->s1._top != 0){StackPush(&obj->s1, x);}else{StackPush(&obj->s2, x);}
}int myQueuePop(MyQueue* obj) {Stack* nonestack = &obj->s1;Stack* _stack = &obj->s2;if (obj->s1._top != 0){nonestack = &obj->s2;}while (_stack->_top>1){StackPush(nonestack, _stack->_a[_stack->_top - 1]);StackPop(_stack);}STDataType tmp = _stack->_a[0];StackPop(_stack);while (nonestack->_top){StackPush(_stack, nonestack->_a[nonestack->_top - 1]);StackPop(nonestack);}return tmp;
}int myQueuePeek(MyQueue* obj) {if (obj->s1._top != 0){return obj->s1._a[0];}else{return obj->s2._a[0];}
}bool myQueueEmpty(MyQueue* obj) {return (obj->s1._top == 0) && (obj->s2._top == 0);}void myQueueFree(MyQueue* obj) {StackDestroy(&obj->s1);StackDestroy(&obj->s2);free(obj);
}

下面是队列

队列的特点的先进先出

即先存储的数据先出去

额,好像也没啥了

下面是其接口函数的实现

#include"Queue.h"void QueueInit(Queue*pq)
{assert(pq);pq->head = NULL;pq->tail = NULL;
}void QueueDestroy(Queue*pq)
{assert(pq);QueueNode*cur = pq->head;while (cur != NULL){QueueNode*next = cur->next;free(cur);cur = next;}pq->head = NULL;pq->tail = NULL;
}void QueuePush(Queue*pq, QDataType x)
{assert(pq);QueueNode*newnode = (QueueNode*)malloc(sizeof(QueueNode));newnode->next = NULL;newnode->data = x;if (pq->head==NULL){pq->head = pq->tail = newnode;}else{pq->tail->next = newnode;pq->tail = newnode;}
}
void QueuePop(Queue*pq)
{assert(pq);assert(!QueueEmpty(pq));QueueNode*next = pq->head->next;free(pq->head);pq->head = next;if (pq->head == NULL){pq->tail = NULL;}
}
QDataType QueueFront(Queue*pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->head->data;
}QDataType QueueBack(Queue*pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->tail->data;
}int QueueSize(Queue*pq)
{assert(pq);int n = 0;QueueNode*cur = pq->head;while (cur){++n;cur = cur->next;}return n;
}bool QueueEmpty(Queue *pq)
{assert(pq);return pq->head == NULL;
}

除此之外还可以用两个队列来实现栈。或两个栈实现队列

单纯怕忘了整理一下

初阶数据结构前部分总结相关推荐

  1. 初阶数据结构 初识二叉树

    初阶数据结构 初识二叉树 一. 树 1. 基本概念 2. 常用术语 3. 代码表示 4. 实际运用 二. 二叉树 1. 基本概念 2. 特殊的二叉树 3. .二叉树的顺序结构及实现 (1)顺序结构 ( ...

  2. 初阶数据结构之带头+双向+循环链表增删查实现(三)

    文章目录 @[TOC](文章目录) 前言 一.带头双向循环链表的初始化 1.1带头双向循环链表的结构体定义 1.2初始化代码的实现 二.带头+双向+循环链表的增功能实现 2.1头插代码的实现 2.2尾 ...

  3. C语言初阶数据结构(四)栈(详细图解,简单上手,通俗易懂)

    目录 今日良言:少年没有乌托邦,心向阳光自明朗 一.各功能实现以及图解+成果展示 1.1栈的结构图解 1.2初始化栈 1.3入栈操作 1.4出栈操作 1.5输出栈内元素 1.6求栈顶元素 1.7输出栈 ...

  4. 二叉树前中后序遍历+刷题【中】【数据结构/初阶/C语言实现】

    文章目录 1. 二叉树基础操作 1.1 二叉树遍历 1.1.1 前序遍历 前序遍历(Pre-Order Traversal) 1.1.2 中序遍历 中序遍历(In-Order Traversal) 1 ...

  5. 树与二叉树(二叉树前传、数据结构初阶、C语言)

    文章目录 前言 一.树的概念及结构 (一).树的概念 (二).树的相关概念 (三).树的表示 二.二叉树的概念及结构 (一).二叉树的概念 (二).满二叉树和完全二叉树 (三).二叉树的性质 (四). ...

  6. 数据结构初阶(4)(OJ练习【判断链表中是否有环、返回链表入口点、删除链表中的所有重复出现的元素】、双向链表LinkedList【注意事项、构造方法、常用方法、模拟实现、遍历方法、顺序表和链表的区别)

    接上次博客:数据结构初阶(3)(链表:链表的基本概念.链表的类型.单向不带头非循环链表的实现.链表的相关OJ练习.链表的优缺点 )_di-Dora的博客-CSDN博客 目录 OJ练习 双向链表--Li ...

  7. 【数据结构初阶】链表(下)——带头双向循环链表的实现

    目录 带头双向循环链表的实现 1.带头双向循环链表的节点类型 2.创建带头双向循环链表的节点 3.向带头双向循环链表中插入数据 <3.1>从链表尾部插入数据 <3.2>从链表头 ...

  8. 数据结构初阶最终章------>经典八大排序(C语言实现)

    前言:   正如标题所言,本篇博客是数据结构初阶的最终章节.但不是数据结构的最终章节!事实上,诸如AVL 树,红黑树这样高阶复杂的数据结构使用C语言非常麻烦,这些数据结构我会放在后续的C++的博客中去 ...

  9. 数据结构初阶:二叉树

    二叉树 链表和数组都是线性结构,而树是非线性的结构. 树是依靠分支关系定义出的一种层次结构.社会亲缘关系和组织结构图都可以用树来形象地表示. 1. 树 1.1 树的定义 树是 n ( n ≥ 0 ) ...

最新文章

  1. leetcode 202 快乐数
  2. django settings 定义的变量不存在_使用Django部署机器学习模型(1)
  3. BAT教程 :第五节(set命令详解)
  4. C++设计模式-备忘录模式
  5. Got a packet bigger than 'max_allowed_packet' bytes(mysql)
  6. java——关于数组的定义 和 访问修饰符的修饰内容
  7. 数据结构(二)之二叉树
  8. DBA 常用的软件工具有哪些(分享篇)?
  9. 【MODIS数据的下载】
  10. java面向对象之实现房屋出租系统
  11. Linux下用dd命令测试硬盘的读写速度
  12. 联想台式电脑出现XXX指令引用了XXX内存 该内存不能为written 问题的解决
  13. 性能测试监控TP50、TP99、TP999含义(99分位延时的含义)
  14. 每日一练 — 2021.12.30
  15. 计算机组成acc什么意思,计算机组成作业老师给的答案[沐风教育]
  16. stm32第一章cortex-M3处理器概述
  17. docker tag 删除images_docker实现重新打tag并删除原tag的镜像
  18. 在计算机桌面怎样写提示语,桌面标语-标语桌面请保持-保持桌面干净标语
  19. 使用MySQL,请用好 JSON 这张牌!
  20. 最近疯狂的爱上了功放

热门文章

  1. 学生派生类 (12 分)
  2. 小车--AGX上移植程序,TensorRT版本升级遇到的问题(7.X---8.0.1.6)
  3. vue 的双向绑定原理
  4. Docker构建镜像
  5. CloudFlare系列--使用第三方来自定义CDN的IP(笨牛简洁版)
  6. Unhandled exception at 0x00007FFE7BFD8A5C in wb.exe: Microsoft C++ exception: std::out_of_range at m
  7. 频谱仪的更改ip_频谱分析系列:三阶交调失真概述及测试
  8. 足疗浴池收银软件如何帮助商店进行良好的市场定位?
  9. 转区系统开放艾欧尼亚转入服务器,终于可以在一起了!转区功能开放申请啦
  10. HTML5的FileReader用法