***抽象数据类型(Abstract Data Type, ADT)*是一些操作的集合。
表ADT
顺序表
不想讲
链表(Linked List)
表头很重要。在位置0处留出一个标志节点,成为表头(header)或者哑结点(dummy node)。
一般来说有两种链表,含表头的与不含表头的,这边只讨论含表头的写法,有时间就自己实现下不含表头的写法。
在这之前补一些C语言的语法:
(不具体展开,就说几个点看看会不会,不会就查查,提供部分参考资料,页码为C PrimerPlus的页码)
- 基本指针操作
- 结构体的声明与初始化
- 指向结构的指针(P449)
- 向函数传递几种不同的结构信息(P451 14.7.1 - 14.7.7)
- typedef (P479)
(链表等ADT资料 P567 高级数据表示)
逻辑都理解,重点就在于如何实现与应用
p573 17.2 就有完整的链表创建,显示,可以先打一遍体验一下
这是不好的链表示范,可以跳过不看
//p573
#include <iostream>
#include <cstdio>
#include <cstdlib> //提供malloc()原型
#include <cstring>
using namespace std;
const int TSIZE = 45;
struct film
{
char title[TSIZE];
int rating;
struct film * next;
};
char * s_gets(char * st, int n);
int main()
{
struct film *head = NULL;
struct film *prev, *current;
char input[TSIZE];
puts("Enter first movie title:");
while(s_gets(input, TSIZE) != NULL && input[0] != '\0')
{
current = (struct film*) malloc (sizeof(struct film));
if(head == NULL)
head = current;
else
prev -> next = current;
current -> next = NULL;
strcpy(current -> title, input);
puts("Enter your rating <0-10>:");
scanf("%d", ¤t -> rating);
while(getchar() != '\n')
continue;
puts("Enter next movie title (empty line to stop):");
prev = current;
}
if(head == NULL)
printf("No data entered. ");
else
printf("Here is the movie list:\n");
current = head;
while(current != NULL)
{
printf("Movie: %s Rating: %d\n",
current -> title, current -> next);
current = current ->next;
}
current = head;
while(current != NULL)
{
free(current);
head = current ->next;
}
printf("Bye!\n");
system("pause");
return 0;
}
char * s_gets(char * st, int n)
{
char * ret_val;
char * find;
ret_val = fgets(st, n, stdin);
if(ret_val)
{
find = strchr(st, '\n');
if(find)
*find = '\0';
else
while(getchar() != '\n')
continue;
}
return ret_val;
}
/*
Input:
Spirited Away
9
THe Duelists
8
Devil Dog: TheMound of Hound
1
Output:
Enter first movie title:
Spirited Away
Enter your rating <0-10>:
9
Enter next movie title (empty line to stop):
THe Duelists
Enter your rating <0-10>:
8
Enter next movie title (empty line to stop):
Devil Dog: TheMound of Hound
Enter your rating <0-10>:
1
Enter next movie title (empty line to stop):
Here is the movie list:
Movie: Spirited Away Rating: 16783552
Movie: THe Duelists Rating: 16783632
Movie: Devil Dog: TheMound of Hound Rating: 0
*/
下面是具体实现链表ADT
遵循无脑接口思想,有手就能用
#include <iostream>
#include <cstdio>
using namespace std;
//定义一个叫做LinkedList的结构布局
typedef struct Node
{
int data;
struct Node *next;
}LinkedList;
/*
接下来都是具有头结点的单链表操作
单链表:list,list既是单链表的名字,也是其头结点
注意这里是头结点,并非头指针形式
*/
//初始化单链表,即建立一个新的空表
//调用函数,无需参数,返回头结点
LinkedList *Initiate ()
{
//内存申请失败极小可能性发生,故在此不判断
LinkedList* list = (LinkedList*)malloc(sizeof(LinkedList)); //创建一个头节点
//初始化头结点,链表为空
list->data = 0;
list->next = NULL;
//返回头结点
return list;
}
//创建储存链表的结点,将元素添加至指向的链表末尾
//调用函数,参数为元素及相应链表的头结点即链表名,返回是否成功
bool AddItem(int element, LinkedList* List)
{
LinkedList *pnew; //新的结点
LinkedList *scan = List; //扫描指针
//申请空间
pnew = (LinkedList*) malloc(sizeof(LinkedList));
//建立结点
pnew->data = element;
pnew->next = NULL;
if(scan->next == NULL) //空链表,把结点放在链表开头
List->next = pnew;
else
{
while(scan->next != NULL) //寻找链表末尾
scan = scan->next;
scan->next = pnew; //向末尾接入结点
}
return true;
}
//按顺序显示整条链表操作
//调用函数,参数为链表名,返回为空
void DisplayAll(LinkedList* List)
{
//注意,头结点只是个头结点,真正的链表要从头结点指向的结点开始
LinkedList* scan = List->next;
while(scan->next != NULL)
{
cout << scan->data << " ";
scan = scan->next;
}
//最后一个结点也要输出才完整
cout << scan->data << endl;
}
//求表长
//调用函数,参数为链表名,返回值为整数
int Length(LinkedList* List )
{
LinkedList *scan; //当前指针
scan = List -> next; //从表头开始,不算头节点
int count = 0; //计数器
while( scan != NULL)
{
count++;
scan = scan -> next;
}
return count;
}
//获取第i个结点元素操作,
//调用函数,给出要第几个以及相应链表
int GetINode(int i, LinkedList* List)
{
LinkedList* scan = List->next;
int j = 1;
while( (scan != NULL) && (j < i) )
{
scan = scan->next;
j++;
}
return scan->data;
}
//定位操作:找到链表中e出现的位置
//调用函数,给出寻找的元素e以及链表名,返回位置,没找到就返回-1
int LocatedE(int e, LinkedList* List)
{
int index = 1;
LinkedList *scan = List->next;
while((scan != NULL) && (scan->data != e))
{
scan = scan -> next;
index++;
}
//没找到返回-1
if(scan == NULL) return -1;
return index;
}
//插入结点操作,比如插入1,那就是第一个结点放置新结点,原链表第一个结点变成第二个
//给出插入的元素,以及插入的位置,以及插入的链表名,返回插入是否成功
bool Insert(int element, int i, LinkedList* List)
{
LinkedList *pnew; //新的结点
LinkedList *scan = List; //扫描指针,注意细节,从头节点开始
int index = 1; //位置
//插入的位置在1~Length之间,否者插入失败
if(i > Length(List) || i < 1) return false;
//扫描为指定位置
while(index < i && scan != NULL)
{
scan = scan->next;
index++;
}
//申请空间
pnew = (LinkedList*) malloc(sizeof(LinkedList));
//建立结点
pnew->data = element;
pnew->next = scan->next;
scan->next = pnew;
return true;
}
//删除元素为e的所有结点
void DelElem(LinkedList* List, int ele)
{
LinkedList* scan = List;
LinkedList* del = NULL;
while(scan->next != NULL)
{
if(scan->next->data == ele)
{
del = scan->next;
scan->next = scan->next->next;
free(del);
}
else
scan = scan->next;
}
}
int main()
{
//声明一个叫mylist的链表
LinkedList* mylist = Initiate();
//将5 4 3 2 1存入相对应的链表中
for(int i = 5; i > 0; i--)
AddItem(i, mylist);
//将链表全部显示出来看看
DisplayAll(mylist);
//输出表长(不包括头结点,这里表长为5)
cout << Length(mylist) << endl;
//看看第二个元素是什么
cout << GetINode(2, mylist) << endl;
//找找2在序列中的位置
cout << LocatedE(2, mylist) << endl;
//将8插入位置1,然后显示链表
if(Insert(8, 1, mylist))
DisplayAll(mylist);
else
cout << "ERROR" << endl;
//删除5,然后显示链表
DelElem(mylist, 5);
DisplayAll(mylist);
system("pause");
return 0;
}
补充一下:
不知道你们明白我的用意没有,链表是一种ADT,按照我的理解,ADT就应该足够抽象,ADT的接口就应该足够简单,实习的功能应该是单一且明确。ADT足够抽象了,我们在使用起来的时候才可以足够的空间来灵活应变,能够直接套用最底层最基础的操作,然后再加上自己不同的使用方式来解决各种各样的问题,要是ADT的不够抽象,比如第一段代码里的链表实现,基本只能用来做这道题,其他题要用就需要大范围动工修改了,那后面我的实现呢,创建就是简单的创建一个空链表,给你一个头结点,然后啥也不多干,你看你做链表的题,怎么样是这样来创建吧,那不是所有题都可以这样来调用这个函数了吗,就不需要对ADT做过多的修改,只是如何使用的问题。好的接口也是同理,不需要过多复杂的参数,只需要最基本的条件,显而易见的调用与返回,使得函数更好更易地使用,这才是一个良好的接口嘛。
这是我看了很多博客里链表实现都难以理解或者我个人认为不够严谨不够好用,就以自己的理解来这样实现这个链表了。