几种线性表(C/C++)


一个小小的目录

  • 几种线性表(C/C++)
  • 基础知识
  • 线性表基础
  • C/C++ 结构struct
  • C/C++指针
  • 线性表
  • 动态内存
  • 代码
  • 1.顺序线性表
  • 2.逆序线性表
  • 3.排序线性表


基础知识

线性表基础

C/C++ 结构struct

在C/C++语言中,我们可以自定义一个类型,所使用的函数就是struct
如,统计学生的信息,比如有姓名,学号,排名等,我们就可以这样定义一个类型,然后对这个类型进行直接操作
其基本形式为:

struct name
{
	int a;
	char b;
	int*p;
};

注意最后花括号也有个分号进行隔断。
当然,struct内部也可以用加上struct。


如何读取和使用struct定义类型内部的数据呢?
有两个方法:
1、"."法。
此方法就是用"."来引出定义类型内部的,某个类型值。
例子:

struct name
{
	int a;
	char b;
	int*p;
};//定义结构name

int num = 0;//声明和初始化;
num = name.a;//取出name结构的int型 a的值;

2、"->"法
此方法需要用到指针,此指针需要是所定义结构类型的指针,然后进行取值。
例子:

struct name
{
	int a;
	char b;
	int*p;
};

name*p;
int num = 0;//初始化
num = p->a;//赋值num 为 name结构的a的值;

C/C++指针

C语言的指针作用强大,涉及对内存地址的处理和使用,若加上动态内存,可以很好的迭代某个过程,如此处的线性表。

线性表

头歌mongodb的安装与简单操作答案 头歌怎么使用_头歌mongodb的安装与简单操作答案

线性表则是很多个相同结构链接起来,一般可以是,一个线性表的节点内部有一个用于储存数据的变量,一个是指针,指向下一个节点,因此我们可以这样写:

struct node
{
int data;
node*next;
}//定义节点含有的数据

node node1;
node*p =&node1;
node node2;

cin>>p->data;
p->next = &node2;//赋值节点一内部的

很快我们可以发现,这样的话我们扩展时十分麻烦;
我们使用动态内存的方式进行迭代。

动态内存

对于C++中, 我们可以用 newdelete 来实现动态。
exa:

node *t = new node;
t->data = 2;
delete t;//每次用完则在分配的内存中删去t;应用于迭代,则可以实现一次一次的将内存动态分配。

代码

线性表虽然的节点是结构相似的,但是其扩展和添加方法是不一样的。主要是以下三种:

1.顺序线性表

线性表前面的顺序逆序排序均是由数据信息得来。
顺序线性表的意思就是按照输入数据的顺序进行线性表建立。
由于开头的指针指向第一个节点,因此我们需要在最后一个节点进行处理,对其内部储存的next指向下一个节点即可。
具体实现可以是:
1、获得头地址指向第一个节点;
2、得到第一个节点内部的next,并指向下一个节点。
3、循环

但是这样下去,我们会发现很麻烦,麻烦在重复确定最后一个节点,再在这个节点后面加新的节点。
于是我们可以每次从头地址,先用头地址定位到第一个节点,然后就用循环。
如何做到呢?

#include "linearList.h"
node *insertTail(node *h, node *t)
{
    if(h==NULL)
    {
        h = t;
        return t;
    }
    node *p =h;
    while(p->next)
    {
        p = p->next;
    }
    t->next = NULL;
    p->next = t;
    return h;
}

每次都历遍到尾部,从尾部添加新的节点,成为顺序插入。

注:此头文件为:

#include <iostream>
using namespace std;

// 定义结点结构
struct node
{
    int data;  // 数据域
    node * next;  //指针域,指向下一个结点
};

// 函数insertTail:链表尾部插入
// 参数:h-链表头指针,t-指向要插入的结点
// 返回值:插入结点后链表的首结点地址
node *insertTail(node *h, node *t);

// 函数printList:输出链表,每个数据之间用一个空格隔开
// 参数:h-链表头指针
void printList(node *h);

// 函数delList:删除链表,释放空间
// 参数:h-链表头指针
void delList(node *h);

测试文件为:

#include "linearList.h"

int main()
{
	int n,i;
	node *t;
	node *head=NULL; // 头指针为NULL,表示线性表为空,结点数为0
	// 输入结点数
	cin>>n;
	for(i=0;i<n;i++)
	{
		// 为新节点动态分配空间
		t = new node;
		cin>>t->data;  // 输入结点数据
		t->next=NULL;  // 结点指针域值为空
		// 调用函数插入结点
		head = insertTail(head, t);
	}
	// 输出链表
	printList(head);
	// 删除结点,释放空间
	delList(head);

	return 0;
}
// 函数delList:删除链表,释放空间
// 参数:h-链表头指针
void delList(node *h)
{
	node *p=h;  // 指针p指向头结点,第一个要删除的结点
	while(p)  // 这个结点是存在的
	{
		h = h->next;  // 头指针h指向下一个结点(下一个结点的地址存在当前结点的指针域中,即h->next中
		delete p;  // 删除p指向的结点
		p = h;  // p指向当前的头结点,即下一个要删除的结点
	}
}
// 函数printList:输出链表,每个数据之间用一个空格隔开
// 参数:h-链表头指针
void printList(node *h)
{
	cout<<"List:"; 
	while(h)
	{// h为真,即h指向的结点存在,则输出该结点的数据
		cout<<" "<<h->data;  // 输出结点数据
		h=h->next;  // 将该结点的指针域赋值给h,h就指向了下一个结点
	}
	cout<<endl; // 输出换行符
}

2.逆序线性表

逆序线性表听起来貌似比顺序更加麻烦,想清楚后并不困难。
逆序只需要每次从head插入节点即可,注意的是,需要逆序的地方是需要将第一个输入节点(即最后一位输出的节点)的next赋值成为head地址,即他的下一个节点为head。并返回此节点到head,然后再添加的节点(倒数第二个),
其代码实现:

#include "linearList.h"

node * insertHead(node *h, node *t)
{
    t->next = h;
    return t;
}

测试文件

#include "linearList.h"

int main()
{
	int n,i;
	node *t;
	node *head=NULL; //头指针为NULL,表示线性表为空,结点数为0
	//输入结点数
	cin>>n;
	for(i=0;i<n;i++)
	{
		//为新节点动态分配空间
		t = new node;
		cin>>t->data; //输入结点数据
		t->next=NULL;  //结点指针域值为空
		//调用函数插入结点到链表头部
		head = insertHead(head, t);
	}
	//输出链表
	printList(head);
	//删除结点,释放空间
	delList(head);

	return 0;
}
//函数delList:删除链表,释放空间
//参数:h-链表头指针
void delList(node *h)
{
	node *p=h; //指针p指向头结点,第一个要删除的结点
	while(p) //这个结点是存在的
	{
		h = h->next; //头指针h指向下一个结点(下一个结点的地址存在当前结点的指针域中,即h->next中
		delete p; //删除p指向的结点
		p = h; //p指向当前的头结点,即下一个要删除的结点
	}
}
//函数printList:输出链表,每个数据之间用一个空格隔开
//参数:h-链表头指针
void printList(node *h)
{
	cout<<"List:"; 
	while(h)
	{//h为真,即h指向的结点存在,则输出该结点的数据
		cout<<" "<<h->data;  //输出结点数据
		h=h->next;  //将该结点的指针域赋值给h,h就指向了下一个结点
	}
	cout<<endl; //输出换行符
}
}

头文件同上顺序线性表
输出的第一个数是的最后一个输入节点,而每次输入的节点的next应指向前一个节点,因此将每个节点当head返回,下一个节点next指向head(即下一个节点),到最后,最后输入节点就当成head返回,printlist函数则从head开始输出每个数值,而最后输入的节点指向倒数第二个输入的节点,完成逆序线性表。

3.排序线性表

排序表可以算是最麻烦的,需要进行数值的比较再进行指针之间的指向。
参考头歌
利用两个指针p q,第一个指针p从head开始,第二个指针q从输入的节点开始,先进行比较,参考数值内的比较排序法,进行next的交换。
此处有三个指针,p,q,t,其中,p指针是用来确定插入节点的头,q指针用来确定插入指针的尾,t为待插入的指针。
首先来一个节点直接接入head,让q指针有值。此处相当于数值排序中max最大值的初始值(例如数组a[100]初始化的max = a[0])。
此处的思想是,判断排序法。利用每个q作为判断值,大于则插入并继续,若小于,则将p值换成q,q值向后寻,以找到大于新插入的数据大小。
判断方式:
三种情况:
第一种:开头,p指针之前没有节点了,只需处理从head处理,判断。
第二种:结尾,则是q运行到末尾,此处判断p。
第三种:在中间,则是利用p、q指针判断前后,前面已存在的节点指向新节点,新节点指向后已存在的节点,形成插入新的节点。可以想象在两根断绳中间利用打结加一根新的,则需首尾两个结。

再需注意的是,寻找新节点位置的方式。由于q指针比p指针先行动,利用q指针进行循环,中间插入判断插入的条件,再是到达尾部的情况和开头的情况即可。

头歌mongodb的安装与简单操作答案 头歌怎么使用_c++_02

#include "linearList.h"

node * insertSort(node *h, node *t)
{
    if(h==NULL)
    {
        h =t;
        return t;
    }
    node*p=NULL;
    node*q = h;
    while(q)
    {
        if(q->data > t->data)
        {
            if(p==NULL)//开头head
            {
                t->next = q;
                return t;
            }
            else//在中间位置
            {
                p->next = t;
                t->next =q;
                return h;
            }

        }
        else//如果if的判断条件不成立,则前推p和q,再次进行比较。
        {
            p =q;
            q = q->next;
        }
    }
    if(q==NULL)//到结尾,现插入的数据一定是最大值(由上述的循环可以得到),此时的p值是最后一个节点,则在p的节点连接新节点即可。
    {
        p->next=t;
        t->next = NULL;
        return h;
    }//记得每个判断完成,即是找到相应情况进行返回。
}

注意这里的return,此处的循环时为了判断新输入的节点应在的线性表位置,而不是利用线性表一直往后推。每个新节点找到自己的对应位置以后即是插入成功了。此时返回头指针,以再次进行判断和插入数据。
因此对应的每种情况找到后进行插入,再即是返回h,再输入新的数据,利用动态地址创新的节点再进行下一次的插入判断和返回了。

测试文件为:

#include "linearList.h"

int main()
{
	int n,i;
	node *t;
	node *head=NULL; //头指针为NULL,表示线性表为空,结点数为0
	//输入结点数
	cin>>n;
	for(i=0;i<n;i++)
	{
		//为新节点动态分配空间
		t = new node;
		cin>>t->data; //输入结点数据
		t->next=NULL;  //结点指针域值为空
		//调用函数插入结点,按data从小到大排序插入
		head = insertSort(head, t);
	}
	//输出链表
	printList(head);
	//删除结点,释放空间
	delList(head);

	return 0;
}
//函数delList:删除链表,释放空间
//参数:h-链表头指针
void delList(node *h)
{
	node *p=h; //指针p指向头结点,第一个要删除的结点
	while(p) //这个结点是存在的
	{
		h = h->next; //头指针h指向下一个结点(下一个结点的地址存在当前结点的指针域中,即h->next中
		delete p; //删除p指向的结点
		p = h; //p指向当前的头结点,即下一个要删除的结点
	}
}
//函数printList:输出链表,每个数据之间用一个空格隔开
//参数:h-链表头指针
void printList(node *h)
{
	cout<<"List:"; 
	while(h)
	{//h为真,即h指向的结点存在,则输出该结点的数据
		cout<<" "<<h->data;  //输出结点数据
		h=h->next;  //将该结点的指针域赋值给h,h就指向了下一个结点
	}
	cout<<endl; //输出换行符
}
}