线性结构的基本特点是除第一个元素无直接前驱,最后一个元素无直接后继之外,其他每个元素都有一个前驱和后继。
2.1线性表(Linear List)的定义和特点:
定义:由n(n>=0)个数据特性相同的元素构成的有限序列称为线性表,n=0时称为空表。
实例:
特点:
称为线性表的首节点,
称为线性表的尾节点
都是
的前驱,其中
是
的直接前驱;
都是
的后继,其中
是
的直接后继;
2.2线性表的逻辑结构
2.3线性表的抽象数据类型定义
ADT List{
数据对象:D = { |
}
数据关系:R = {}
基本操作:
操作名1:
初始条件
操作结果1
.
.
.
}ADT List
2.4线性表的顺序存储和实现
顺序存储:把线性表的节点按逻辑顺序依次存放在一组地址连续的存储单元里,用这种方法存储的线性表简称顺序表。
特点:
线性表的逻辑顺序与物理顺序一致
数据元素之间的关系是以元素在计算机内“物理位置相邻”来体现。
顺序表的基本操作:
C语言定义结构体
1)顺序线性表初始化
2)顺序线性表的插入
js代码实现顺序线性表的插入:
3)顺序线性表的删除
js代码实现顺序线性表的删除
2.5线性表的链式存储
链式存储:用一组任意的存储单元存储线性表中的数据元素,用这种方法存储的线性表简称线性链表。
存储链表中节点的一组任意存储单元可以是连续的,也可以是不连续的,甚至零散分布至内存的任意位置上
链表中节点的逻辑顺序和物理顺序不一定相同。
为了正确表示结点间的逻辑关系,在存储每个结点值的同时,还必须存储指示其直接后继节点的地址,C中称为指针,节点值(数据域)和地址(指针)两者组成链表中的结点结构。
链表通过每个结点的地址将线性表的n个结点按其逻辑顺序连接在一起。
每一个结点只包含一个地址域的链表称为单链表。
为操作方便,总是在链表的第一个结点之前附设一个头节点head指向第一个结点,头节点的数据域可以不存储任何信息(或链表长度信息)。
下图可以看出链表中节点的逻辑顺序和物理顺序不一定相同。
结点的描述与实现
C语言中,用带指针的结构体来描述
typedef struct Lnode{
ElemType data;//数据域,保存结点的值
struct Lnode *next;//指针域
}LNode; //结点的类型
结点的实现:
结点是通过动态分配指针和释放来实现,即需要时分配,不需要时释放。是现实是分别使用C语言提供的标准函数:malloc(),realloc(),sizeof(),free()。
动态分配:
p = (LNode*)malloc(sizeof(LNode));
函数malloc分配了一个类型为LNode的结点变量的空间,并将其首地址放如指针变量p中。
动态释放:free(p)
系统回收由指针变量p所指向的内存区,p必须是最近一次调用malloc函数时的返回值。
最常见的基本操作及其示意图
2.6单线性链表的基本操作
单项现象链表的基本操作
<script>
//设计一个基于对象的链表
//创建链表Node类
function Node(element){
this.element = element;
this.next = null;
}
//创建链表/增删改查
function LinkedList(){
this.head = new Node('head');//创建头结点
this.find = find; //查找结点
this.insert = insert;//插入结点
this.findPrev = findPrev;//找到某结点的前一结点
this.remove = remove;//删除结点
this.display = display;//显示链表元素
//查找对应item的结点并返回
function find(item){
var currentNode = this.head;//从头节点开始搜索
//循环遍历,直至找到item结束
while(currentNode.element!=item){
currentNode = currentNode.next;
}
//返回item的结点
return currentNode;
}
//在item后插入新元素
function insert(newElement,item){
var newNode = new Node(newElement);//定义新元素结点
var current = this.find(item);//从链表中找到item结点
newNode.next = current.currentNode;//将item元素的结点后继赋值给新元素结点的后继
current.next = newNode;//item元素的后继插入新元素
}
//显示链表
function display(){
var currentNode = this.head;
while(!(currentNode.next==null)){
console.log(currentNode.next.element);
currentNode = currentNode.next;
}
}
//删除链表的item结点元素
//1.找到item结点的前一个结点
function findPrev(item){
var currentNode = this.head;//定义待删除结点的前一个结点
/*
算法分析:
循环遍历currentNode的下一个结点,如果找到对应等于item的currentNode.next.element,
while循环结束返回当前的currentNode,即待删除元素的前一个结点
*/
while(!(currentNode.next==null) && currentNode.next.element!=item){
currentNode = currentNode.next;
}
return currentNode;
}
//2.删除item阶段元素
function remove(item){
var prevNode = this.findPrev(item);//找到待删除结点的前一结点
if(!(prevNode.next==null)){
prevNode.next = prevNode.next.next;
}
}
}
//测试程序
var cities = new LinkedList();
cities.insert('北京','head');
cities.insert('上海','北京');
cities.insert('广州','上海');
cities.insert('天津','广州');
cities.display();//北京 上海 广州 天津
cities.remove('北京');
cities.display();//上海 广州 天津
console.log(cities.find('上海').next)//Node {element: "广州", next: Node}
console.log(cities.find('天津').next);//undefined
</script>
双向链表的基本操作
<script>
//设计一个基于对象的双向链表
//创建链表Node类
function Node(element){
//data数据
this.element = element;
//后继地址
this.next = null;
//前驱地址
this.previous = null;
}
//创建链表/增删改查
function LinkedList(){
this.head = new Node('head');//创建头结点
this.find = find; //查找当前结点
this.insert = insert;//插入结点
this.findPrevious = findPrevious;//找到某结点的前一结点
this.remove = remove;//删除结点
this.display = display;//显示链表元素
this.findLast = findLast;//查找链表最后一个元素
this.dispReverse = dispReverse;//反向显示
//查找对应item的结点并返回
function find(item){
var currentNode = this.head;//从头节点开始搜索
//循环遍历,直至找到item结束
while(currentNode.element!=item){
currentNode = currentNode.next;
}
//返回item的结点
return currentNode;
}
//在item后插入新元素
function insert(newElement,item){
var newNode = new Node(newElement);//定义新元素结点
var current = this.find(item);//从链表中找到item结点
newNode.next = current.currentNode;//将item元素的结点后继赋值给新元素结点的后继
newNode.previous = current;//将item元素赋值给新元素结点的前驱
current.next = newNode;//item元素的后继插入新元素
}
//显示链表
function display(){
var currentNode = this.head;
while(!(currentNode.next==null)){
console.log(currentNode.next.element);
currentNode = currentNode.next;
}
}
//删除链表的item结点元素
//1.找到item结点的前一个结点
function findPrevious(item){
var currentNode = this.head;//定义待删除结点的前一个结点
/*
算法分析:
循环遍历currentNode的下一个结点,如果找到对应等于item的currentNode.next.element,
while循环结束返回当前的currentNode,即待删除元素的前一个结点
*/
while(!(currentNode.next==null) && currentNode.next.element!=item){
currentNode = currentNode.next;
}
return currentNode;
}
//2.删除item阶段元素
function remove(item){
var currentNode = this.find(item);//找到当前待删除结点
if(!(prevNode.next==null)){
currentNode.previous.next = currentNode.next;//前一个元素结点的的后继指向当前元素的下一个元素结点
currentNode.next.previous = currentNode.previous//后一个元素结点的前驱指向当前元素的前一个元素结点
currentNode.next = null;//当前结点后继为空
currentNode.previous = null;//当前结点前驱为空
}
}
//双向链表查找最后一个结点
function findLast(){
var currentNode = this.head;//从头节点开始搜索
//循环遍历,直到地址为空
while(!(currentNode.next==null)){
currentNode = currentNode.next;
}
//最后的结点
return currentNode;
}
//反序显示双向链表中的元素
function dispReverse(){
var currentNode = this.findLast();
while(!(currentNode.previous==null)){
console.log(currentNode.element);
currentNode = currentNode.previous;
}
}
}
//测试程序
var cities = new LinkedList();
cities.insert('北京','head');
cities.insert('上海','北京');
cities.insert('广州','上海');
cities.insert('天津','广州');
cities.display();//北京 上海 广州 天津
cities.dispReverse();//天津 广州 上海 北京
console.log(cities.find('上海').next)//Node {element: "广州", next: Node}
console.log(cities.find('天津').next);//undefined
</script>
2.7循环链表
循环链表是一种头尾相接的链表,其特点是最后一个结点的指针指向链表的头节点,整个链表的指针域连接成一个环。
从循环链表的任意一个结点出发,都可以找到链表中的其他结点。