一、算法的基本知识
1.编写程序的基本规则
①尽量少使用内存空间。②尽量少的代码量解决问题。
2.数据的特点
①必须能够输入到计算机。②必须能够被程序处理。
3.数据的几个概念
①数据元素:组成数据的基本单位。②数据项:一个元素由若干个数据组成。③数据对象:性质相同的数据元素的集合。
如下图:
4.数据的结构的组成
(1)逻辑结构:集合结构、线性结构、树形结构、图形结构
(2)物理结构:
①顺序存储结构:将数据存储在连续的存储单元里。(例如数组)
②链式存储结构:将数据存储在任意的存储单元里,通过保存地址的方式找到相关联的数据。
5.数据结构静态描述了数据元素之间的关系。高效的程序需要在数据结构的基础上设计合理的算法。
算法的概念:是特定的问题的求解步骤的描述。在计算机中表现为指令的有序序列。
6.算法的特性:(1)输入(2)输出(3)有穷性(4)确定性(5)可行性
7.程序 = 数据结构 + 算法。
8.算法的效率表示方法采用大O表示法。
概念:大O的表示法(O()),也就是查看最高次项的表示法。分析的算法的时间复杂度,指的都是算法的最坏时间复杂度。
算法的空间复杂度:
S(n) = O(f(n))
大O表示方法如下:
二、线性表的基本概念
1.定义:
(1)线性表是由0个或者多个数据元素组成的集合。
(2)线性表中数据元素之间是有顺序的。
(3)线性表中的数据元素的个数必须有限。
(4)线性表中的数据元素类型必须相同。
线性表是具有相同类型的n个数据元素的有限序列。
2.特性:
(1)第一个元素只有一个后继
(2)线性表的最后一个元素只有一个前驱
(3)除了a0和an以外其他的元素都应该既有前驱又有后继
(4)线性表能够逐项访问和顺序存取
3.线性表的相关操作:
(1)创建
(2)销毁
(3)得到长度
(4)从线性表删除一个元素
(5)从线性表添加一个元素
(6)在线性表的特定位置添加元素
三、线性表的操作和具体实现
1.线性表的顺序存储定义:用一段地址连续的内存单元依次存储线性表的数据元素。
2.线性表的具体操作:
(1).h文件
#ifndef _1_H_
#define _1_H_
//这里是一个类型封装,对外接口都是void*。
typedef void SeqList;
typedef void SeqListNode;
//定义一个线性表的最大容量
SeqList* SeqList_Create(int capacity);
void SeqList_Destroy(SeqList* list);
void SeqList_Clear(SeqList* list);
int SeqList_Length(SeqList* list);
//返回顺序表的最大容量
int SeqList_Capacity(SeqList* list);
int SeqList_Insert(SeqList* list, SeqListNode* node, int pos);
SeqListNode* SeqList_Get(SeqList* list, int pos);
SeqListNode* SeqList_Delete(SeqList* list, int pos);
#endif
(2)数据结构的.c文件
#include <stdio.h>
#include <malloc.h>
#include "1.h"
typedef unsigned int TSeqListNode; //存放的是数据地址,所以使用unsigned int
typedef struct student
{
int length; //顺序表的长度
int capacity;//顺序表的容量(自己定义,也是一个缺陷)
TSeqListNode *node; //用于顺序表中的数据进出
}TSeqList;
SeqList* SeqList_Create(int capacity)
{
TSeqList * ret = NULL;
if (capacity >= 0)
{
ret = (TSeqList*)malloc(sizeof(TSeqList) + sizeof(TSeqListNode)*capacity);
}
if (ret != NULL)
{
ret -> length = 0;
ret -> capacity = capacity;
ret -> node = (TSeqListNode*)(ret + 1);
}
return ret;
}
void SeqList_Destroy(SeqList* list)
{
free (list);
}
void SeqList_Clear(SeqList* list)
{
TSeqList* slist = (TSeqList*)list;
if (slist != NULL)
{
slist -> length = 0;
}
}
int SeqList_Length(SeqList* list)
{
TSeqList * slist = (TSeqList *)(list);
int ret = -1;
if (slist != 0)
{
ret = slist -> length;
}
return ret;
}
int SeqList_Capacity(SeqList* list)
{
TSeqList* slist = (TSeqList*)list;
int ret = -1;
if (slist != NULL)
{
ret = slist -> capacity;
}
return ret;
}
int SeqList_Insert(SeqList* list, SeqListNode* node, int pos)
{
TSeqList *slist = (TSeqList*)list;
int ret = (slist != NULL);
int i = 0;
ret = ret && (slist->length + 1 <= slist -> capacity);
ret = ret && (pos >= 0);
if (ret)
{
if (pos >= slist -> length) //如果插入数据的位置比表的长度还长,那么就默认到表的结尾。
{
pos = slist -> length;
}
for (i = slist -> length; i > pos; i--)
{
slist->node[i] = slist->node[i - 1];
}
slist -> node[i] = (TSeqListNode)node; //对要插入的地址进行强制类型转换
slist -> length++;
}
return ret; //1成功,0失败。
}
SeqListNode* SeqList_Get(SeqList* list, int pos)
{
TSeqList * slist = (TSeqList*)list;
SeqListNode * ret = NULL;
if (slist != NULL && pos >= 0 && pos < slist -> length)
{
ret = (SeqListNode*)(slist->node[pos]);
}
return ret;
}
/*不做过多的注释了,个人觉得删除函数需要注意的地方更多。*/
SeqListNode* SeqList_Delete(SeqList* list, int pos)
{
TSeqList *slist = (TSeqList*)list;
SeqListNode * ret = SeqList_Get (list, pos);
int i = 0;
if (ret != NULL)
{
//ret = slist -> node[pos];
for (i=pos+1; i<slist->length; i++)
{
slist -> node[i - 1] = slist-> node[i];
}
slist -> length--;
}
return ret;
}
(3)测试部分代码.c文件
#include <stdio.h>
#include "1.h"
#include <stdlib.h>
int main()
{
SeqList *list = SeqList_Create(9);
int i = 0;
int j = 1;
int k = 2;
int x = 3;
int y = 4;
int z = 5;
int index = 0;
SeqList_Insert(list, &i,0);
SeqList_Insert(list, &j,0);
SeqList_Insert(list, &k,0);
SeqList_Insert(list, &x,0);
SeqList_Insert(list, &y,0);
SeqList_Insert(list, &z,0);
for (index = 0; index < SeqList_Length(list); index++)
{
int *p = (int*)SeqList_Get(list, index);
printf ("%d\n",*p);
}
printf("\n");
while( SeqList_Length(list) > 0 )
{
int* p = (int*)SeqList_Delete(list, 0);
printf("%d\n", *p);
}
SeqList_Destroy(list);
return 0;
}
以下都是我同学的经典总结:大家可以借鉴。
本节的代码是一个可以适合各种类型的顺序表,之所以能够适合各种类型,是因为它在顺序表中保存的是元素的地址(其实就是一个指针数组)。
关于老唐的后期补充:
我们先来更改一下测试代码:
#include <stdio.h>
#include "1.h"
#include <stdlib.h>
int main()
{
SeqList *list = SeqList_Create(9);
int i = 0;
int j = 1;
int k = 2;
int x = 3;
int y = 4;
int z = 5;
int index = 0;
SeqList_Insert(list, &i,0);
SeqList_Insert(list, &j,0);
SeqList_Insert(list, &k,0);
SeqList_Insert(list, &x,0);
SeqList_Insert(list, &y,0);
SeqList_Insert(list, &z,0);
for (index = 0; index < SeqList_Length(list); index++)
{
int *p = (int*)SeqList_Get(list, index);
printf ("%d\n",*p);
}
printf("\n");
int* p = (int*)SeqList_Delete(list, 5);
printf("%x\n", SeqList_Get(list, 5));
SeqList_Destroy(list);
return 0;
}
这段代码实现的功能:我们首先在顺序表中插入6个元素分别是0,1,2,3,4,5.然后我们执行打印操作,在对第5个位置的元素进行删除,这样实际上我们删除的是数值为5的元素,也就是删除顺序表末尾的元素,然后我们再获取已经删除了的元素的地址,使用SeqList_Get函数,这个时候我们打印出来的是0,而老唐原来的程序打印出来的是一个地址,但是假如打印出来的是地址,但是这个数据元素我们已经删除了它还可以打印出来地址,这是不对的,所以我们要注意get函数中的条件检测。get函数中的安全性检测,如下所示:
SeqListNode* SeqList_Get(SeqList* list, int pos)
{
TSeqList * slist = (TSeqList*)list;
SeqListNode * ret = NULL;
if (slist != NULL && pos >= 0 && pos < slist -> length)
{
ret = (SeqListNode*)(slist->node[pos]);
}
return ret;
}
这里一定要注意 pos < slist->length,而不是pos<=slist->length,因为我们的长度是从1开始算的,而我们的位置却是从0开始算的。