【数据结构篇02】线性表

目录

基础定义:

代码演示:

多说几句:


基础定义:

  • 线性表:是具有相同特性数据元素的有限序列,所含个数叫做线性表的长度,n为0时,是一个空表。线性表开头的结点称为表头(head)结尾结点称为表尾(tail),元素与它的位置有联系也可以无联系,例如有序线性表(递增顺序排列),无序线性表,除表头和表尾元素之外,其他元素只有一个前驱也只有一个后驱,这是线性表的逻辑特性

  • 线性表的存储结构:顺序存储结构(顺序表)、链式存储结构(链表)

    • 顺序表:把线性表中所有元素按照逻辑顺序,依次存储到从指定的存储位置开始的一块连续的储存空间中。

    • 链表:链表存储中,每个结点不仅包含所有元素的信息,还包含元素之间的逻辑关系的信息,例如前驱结点包含后继结点的地址信息。链表有五种以下形式:

      1. 单链表:---,编码中可以采用尾插法,头插法创建

      2. 双链表:---

      3. 循环单链表:最后一个结点的指针域指向第一个结点

      4. 循环双链表:在上述基础上,第一个结点的prev指针指向最后一个结点。

      5. 静态链表:借助一维数组实现的链表。与一般链表结构区别:一般链表结构节点空间来自整个内存,静态链表则来自于一个结构体数组

  • 两种存储结构比较:

    • 顺序表特性:随机访问特性(只要知道0点位置,就能找到任何一个元素的位置),占用连续的储存空间(预先一次性分配,操作过程中始终不变)

    • 链表特性:不支持随机访问,读取某个元素必须遍历,储存空间效率稍低于顺序表,链表存储空间支持动态分配,插入删除元素效率更高。

推荐一个数据结构演示网站https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

 

代码演示:

只演示一些个人认为有必要实践的代码,来实现一下这题吧:

代码内容:单链表结点结构定义尾插法创建单链表(有头结点),打印单链表,递增顺序合并单链表。

Q:A和B都是两个单链表,其中元素递增,将其合并为仍然有序的链表

#include<iostream>
using namespace std;

//#define MAXSIZE 100
定义顺序表
//typedef struct {
//	int data[MAXSIZE];
//	int length;
//}SList;

//单链表结点结构体
typedef struct LNode {
	int data;
	struct LNode* next;
}LNode;

//用来初始化单链表
int A[10] = { 2,6,7,9,11,12,14,24,44,55 };
int B[5] = { 8,16,25,27,30 };

//尾插法建立一个单链表
void initLListR(LNode *&llist,int a[],int n) {
	LNode* newNode, *last; //last表示尾结点
	llist = (LNode*)malloc(sizeof(LNode));//申请一个头结点空间
	llist->next = NULL;
	last = llist; //开始时候尾结点和头结点是一个

	for (int i=0; i < n; i++) {
		newNode = (LNode*)malloc(sizeof(LNode));
		newNode->data = a[i];
		newNode->next = NULL; //这样写也可以①
		last->next = newNode;
		last = last->next;
	}
	//last->next = NULL; 书二中的写法②
}

//打印链表
void printLList(LNode* llist) {
	LNode* temp = llist->next; //从头结点下一个打印
	while (temp!= NULL)
	{
		cout << temp->data <<" ";
		temp = temp->next;
	}
	cout << endl;
}

//顺序合并
void merge(LNode* A, LNode* B, LNode*& C) {
	LNode* a = A->next;
	LNode* b = B->next;
	LNode* temp;
	C = A; //用A的头结点作为合并后的头结点
	C->next = NULL;
	//free(B) 可以在所有程序完成后同意释放
	temp = C;
	//比大小一个一个合并
	while (a!=NULL&&b!=NULL)
	{
		if (a->data < b->data) {
			temp->next = a;
			a = a->next;
		}
		else {
			temp->next = b;
			b = b->next;
		}
		temp = temp->next;
	}
	//temp->next = NULL;
	//将剩余的链表链接在合并链表后
	if (a != NULL) {
		temp->next = a;
	}
	if (b != NULL) {
		temp->next = b;
	}
}

int main() {
	LNode *AL,*BL,*CL;
	//尾插法创建两个新链表
	initLListR(AL, A, 10);
	initLListR(BL, B, 5);
	//打印两个单链表
	cout << "AL:";
	printLList(AL);
	cout << "BL:";
	printLList(BL);
	//顺序合并
	merge(AL, BL, CL);
	//打印合并后的单链表
	cout << "CL:";
	printLList(CL);
	return 0;
}

运行结果:

后面类似的单链表操作以及双链表能够理解其机制(参考书二)自己编码实现即可!

多说几句:

单链表、双链表的定义,一些操作例如生成链表、插入元素、删除元素、打印元素、查找元素等等都应该是必须掌握其机制,主要要理解其指针到底怎么操作的。

咋们来继续看一个问题——逆置问题

何为逆置问题:这样来理解,设置两个变量iji指向第一个元素,j指向最后一个元素,边交换i和j所指的元素,相向而行,知道相遇,说实话有点想浪漫~

对于一个数组,操作很简单,更难的一些操作参见书二,有具体的代码示范。

	for (int i = lef, j = right; i < j; ++i, ++j) {
		temp = a[i][j];
		a[i] = a[j];
		a[j] = temp[i];
	}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值