(可应用于大量有规律性数据,比如轮组转速与电流关系;浓度与检测值关系做出线性链表,可用moto模拟)
线性表的链式存储结构
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映像)+指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。
1.链接存储方法
链接方式存储的线性表简称为链表(Linked List)
链表的具体存储表示为:
1.用一组任意的存储单元来存放线性表的结点(这组存储单元既可以是连续的,也可以是不连续的)
2.链表中结点的逻辑次序和物理次序不一定相同。为了能正确表示结点间的逻辑关系,在存储每个结点值的同时,还必须存储指示其后继结点的地址(或位置)信息(称为指针(pointer)或链(link))
链式存储是最常用的存储方式之一,它不仅可用来表示线性表,而且可用来表示各种非线性的数据结构。
2、结点结构
data域——存放结点值的数据域
next域——存放结点的直接后继的地址(位置)的指针域(链域)
带头结点的单链表:头结点的data不保存信息,next指针指向链表的第一个具有data域结点。
链表通过每个结点的链域将线性表的n个结点按其逻辑顺序链接在一起的,每个结点只有一个链域的链表称为单链表(Single Linked List)。单链表的最大特点是可以将物理地址上不连续的数据连接起来,通过指针来对物理地址进行操作,实现增删改查等功能。
线性表链式存储结构的建立
typedef struct Node
{
int data;
struct Node *next;
}Node,*List;
2、主要操作
void InitList(List plist);//初始化单链表
bool Insert_head(List plist,int val);//头插法
bool Insert_tail(List plist,int val);//尾插法
bool Insert_pos(List plist,int pos,int val);//pos 位置插入
Node *Search_pre(List plist,int key);//查找 key 的前驱
bool Delete(List plist,int key);//删除 key 这个结点
bool IsEmpty(List plist);//是否为空
void Destroy(List plist);//摧毁函数(如果有动态开辟内存)
int GetLength(List plist);//得到单链表的长度
void Show(List plist);//打印单链表
1.初始化单链表
void initList(List plist)
{
assert(plist != NULL);
plist->next = NULL;
}
2.得到一个节点
static Node *getNode(int val)
{
Node *pGet = (Node*)malloc(sizeof(Node));
assert(pGet != NULL);
pGet->data = val;
pGet ->next = NULL;
return pGet;
}
3.头插法
要注意与放入的数据顺序相反
bool Insert_head(List plist,int val)//头插法
{
Node *pGet = getNode(val);
pGet->next = plist->next;
plist ->next = pGet;
return true;
}
4.尾插法
操作注意 要用的一个pcur指针指向尾部
与放入的数据顺序相同
bool Insert_tail(List plist,int val)//尾插法
{
Node *pcur = plist;
while(pcur -> next!=NULL)
{
pcur = pcur->next;
}
Node *pGet =getNode(val);
pGet->next = pcur ->next;
return true;
}
5.pos位置插入
bool Insert_pos(List plist,int pos,int val)//pos位置插入
{
assert(plist !=NULL);
if(pos<0||pos>GetLength(plist))
{
return false;
}
Node *pGet = getNode(val);
Node *pcur = plist->next;
for(int i =0;i<pos;i++)
{
pcur =pcur->next;
}
pGet->next = pcur->next;
pcur->next = pGet;
return true;
}
一.链表的创建
因为我希望在创建结点的时候按照某种方法去排序
typedef struct node{
int data;
struct node*next;
}Node;
然后先让我们看看主函数中该怎么写
#include<stdio.h>
#include<stdlib.h>
int main(){
Node *head;
heda=NULL;
int n;//z这里的n代表创建多少个结点
scanf("%d",&a);
for(int i = 0;i<n;++i)
{
int a;//这里的a代表传给结点的值
scanf("%d",&a);
insert(&head,a);//insert增加链表结点函数
}
Node *temp = head;
while(temp != NULL)
{
//输出这个链表
printf("%d",temp->next);
}
return 0;
}
接下来就是inser()函数的写法了
首先我们需要在这个函数里面创建一个结点
void insert(Node **head,int value){//这个参数是取头结点的地址
Node *temp = (Node*)malloc(sizeof(Node));
temp->next = NULL;
temp->data = value;
}
然后怎么利用头结点的头插法把这个链表连起来呢?
我们首先得判断 这个头结点是不是为NULL 因为在定义的时候定义了NULL
if((*head)==NULL)//这里*head 因为head是二级指针 加个*就降了一级.
{
(*head) = temp;//建议全部带括号*这个运算符的结合律防止出错都带括号把
}
其实这个判断也就是判断是不是第一次创建 头结点有没有东西
头结点要是有了东西呢?
else
{
Node *t=(*head);//让临时指针的从头开始遍历
while(t->next != NULL)
{//这里一定是t->next 如果让t连的话就连接不起来
t=t->next;
}
t->next=temp;//指针域的作用就是连接
}
好了 这就是头插法的创建
但是我们明显发现效率不高 尾插法创建一个就接到链表里面
创建一个就接进去.
但是头插法不行. 必须是创建一个 再从头开始.
一个妙用
我要创建链表并且排序.
这里我们做一个约定 从小到大排列.让我们看看怎么做
还是像刚才创建一样 如果头是空怎么怎么样 否则怎么怎么样对吗?
想一想要改这里吗?
if((*head)==NULL)
{
(*head) = temp;
}else
{
}
答案肯定是不用改因为没数据肯定就把第一个放进去.
那么肯定就是改下一处了
按照我们刚在的想法是不是可以这样写
while(t !=NULL)
{
if(t->next = temp->data)
{//如果t的下一个的数据大于了新结点
temp->next=t->next;//让新结点的指针指向了 t的下一个
t->next = temp;//再让T指向新结点
return;//返回就好
}
t = t->next;
}
但是这里其实是有两处问题的 先说第一处:
因为是从小到大比较的 我们上面的写法是从头开始
找到比他大的地方就让他插进去.但是如果新的结点就是最大的呢
也就是跑到完都没进到 if 语句中去
所以应该加一个
while(t !=NULL)
{
if(t->next == NULL)
{//这一次我们判断下一个是不是空
t->next = temp;//如果是空了连接一下
return;//返回就行
}
else if(t->next->data>temp->data)
{
temp->next = temp;
return;
}
t = t->next;
}
上述不要忘记了返回
还有一个问题就是如果新的结点比头结点小了怎么办
看看我们上面的写法他永远比较的是当前结点的下一个 也就是头结点根本没有比较
所以我们让他在开始的时候比较一下就好了
if(temp->data <(*head)->data)
{
temp->next = (*head);//新结点的下一个指向头结点
(*head) = temp;//头结点变成了新结点
return;//返回
}
好了 这里就写完了 开发的时候具体按照你的方式去创建链表 要比较什么 你自己定
完整代码
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
int data;
struct node*next;
}Node;
void insert(Node **head,int value)
{
Node *temp = (Node*)malloc(sizeof(Node));
temp->next = NULL;
temp->data = value;
if((*head) == NULL)
{
(*head) = temp;
}
else
{
Node *t = (*head);
if(temp->data < (*head)->data)
{
temp->nxet = (*head);
(*head) = temp;
return;
}
while(t !=NULL)
{
if(t->next == NULL)
{
t->next == temp;
return;
}
while(t !=NULL)
{
if(t->next ==NULL)
{
t->next = temp;
return;
}
while(t !== NULL)
{
if(t->next == NULL) {
t->next = temp;
return;
} else if(t->next->data > temp->data) {
temp->next = t->next;
t->next = temp;
return;
}
t = t->next;
}
}
}
}
}
int main() {
Node *head;
head = NULL;
int n;//这里的n代表创建多少个结点
scanf("%d",&n);
for (int i = 0; i < n; ++i) {
int a;//这里的a代表传给结点的值
scanf("%d",&a);
insert(&head, a);//insert 增加链表结点函数
}
Node *temp = head;
while(temp != NULL) { //输出这个链表
printf("%d ",temp->data);
temp = temp->next;
}
return 0;
}
头插法讲完了 让我们讲一下尾插法
先看一下 一般的创建方式
首先尾插法的核心代码
2020年08月09
1+2+3+…100
计算100次
int i,sum=0,n=100
for(i=1;i<=n;i++)
{
sum=sum+i;
}
printf("%d",sum);
计算一次
int i,sum=0,n=100
sum=(1+n)*n/2;
printf("%d",sum);
算法时间复杂度
算法时间复杂度的定义:在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度,也就是算法的时间量度,计做:T(n)=Of(n)它表示随问题n的增大,算法执行时间的增长率和F(n)的增长率相同,称作算法的渐进时间复杂度,简称时间复杂度。其中f(n)是问题规模n的某个函数。
一般情况下,随着输入规模n的增大,T(n)增长最慢的算法为最优算法。
如何分析一个算法的时间复杂度?即如何推导大O阶呢?
用常数1取代运行时间中的所有加法常数。
在修改后的运行次数函数中,只保留最高阶项
如果最高阶项存在且不是1,则去除与这个项相乘的常数
得到最后结果就是最大0阶
平方阶
时间复杂度O(n*2)
对数阶
O(logn)
O(n)
算法的空间复杂
线性表
线性表:(LIst)由零个或多个数据元素组成的有限序列
线性表的顺序存储结构
#define ERROR 0;
#define OK 1;
//Status是函数的类型
Status GetElem(Sqlist L,int i,ElemType *e)
{
if(L.length==0||i<1||i<L.length)
{
return ERROR;
}
*e = L.data[i-1];
return OK;
}
Sqlist L线性表
int i索引位置
ElemType *e存放指针
插入操作
ListInsert(*L,i,e),即在L列表中插入个新元素e
Status ListInsert(SqList *L,int i,ElemType e)
{
int k;
if(L->length == MAXSIZE)
{
return ERROR;
}
if (i<1 || i>L->length+1)
{
return ERROR;
}
if(i<=L->length-1;k>=i-1;k--)
{
L->data[k+1] = L->data[k];
}
L->data[i-1] = e;
L->length++;
retrun ok;
}
静态链表的插入操作
首先获取空闲分量的下标
int Malloc_SLL(StaticLinkList space)
{
int i=space[0].cur;
if(space[0].cur)
space[0].cur = space[i].cur;
//把它下一个分量用来作为备用
return i;
}
在静态链表L中第i个元素之前插入新的数据元素e
Status ListInsert(StaticLinkList L,int i, ElemType e)
{
int j,k,l;
k=MAX_SIZE-1;//数组的最后一个元素
if(i<1||i>ListLength(L)+1)
{
return ERROR;
}
j=malloc_SLL(L);
if(j)
{
L(j).data = e;
for(l=1;l<=i-1;l++)
{
k = L(k).cur;
}
L[j].cur = L[k].cur;
L[k].cur = j;
return OK;
}
return ERROR;
}
静态链表的删除操作
Status LisDelete(StaticLinkList L,int i)
{
int j,k;
if(i<1||i>ListLength(L))
{
return ERROR;
}
k = MAX_SIZE-1;
for(j=1;j<=i-1;j++)
{
k= L[k].cur;
}
j=L[k].cur;
L[k].cur = L[j].cur;
Free_SLL(L,j);
return OK;
}
void Free_SLL(l,j);
{
space[k]cur=space[0].cur;
space[0].cur=k;
}
//返回L中数据元素个数
int ListLength(staticLinkList L)
{
int j=0;
int i = L[MAXSIZE-1].cur;
while(i)
{
i = L[i].cur;
j++;
}
return j;
}
%取余,/取整
2/11 =0;2%11=2
6%4=2;(-6)%4=2;6%(-4)=-2
6/3=2;(-6)/3=-2;6/(-3)=-2