华科大考研计算机系834大纲之数据结构(二)

本节大纲内容

  • 线性表的概念,线性表的抽象数据类型,基本操作

    线性表

    线性表是具有相同数据类型的n(n>=0)个数据元素的有限序列。除第一个元素外,每个元素有且仅有一个直接前驱。除最后一个元素外,每个元素有且仅有一个直接后继。
    这种线性有序的逻辑结构正是线性表名字的由来。

    线性表的特点
    (1)表中元素的个数有限。
    (2)表中元素具有逻辑上的顺序性,在序列中各个元素排序有先后次序。
    (3)表中元素都是数据元素,每一个元素都是单个元素。
    (4)表中元素的数据类型都相同。这意味着每一个元素占有相同大小的存储空间。
    (5)表中元素具有抽象性。即仅讨论元素间的逻辑关系,不考虑元素究竟表示什么内容。

    注意
    (1)线性表是一种逻辑结构,表示元素之间一对一的相邻关系。
    (2)顺序表和链表是指存储结构,两者属于不同层面的概念,因此不要将其混淆。

    线性表的基本操作
    一个数据结构的基本操作是指 其最核心、最基本的操作。其他较复杂的操作可以通过调用其基本操作来实现。
    (1)InitList(&L):初始化表。构造一个空的线性表。
    (2)Length(L):求表长。返回线性表L的长度,即L中数据元素的个数。
    (3)Locate(L,e):按值查找操作。在表L中查找具有给定关键值的元素。
    (4)GetElem(L,i):按位查找操作,获取表L中第i个位置的元素值。
    (5)ListInsert(&L,i,e):插入操作。在表L中第i个位置上插入指定元素e。
    (6)ListDelete(&L,i&e):删除操作。删除表中第i个位置的元素,并用e返回被删除元素的值。
    (7)PrintList(L):输出操作。按前后顺序输出线性表L的所有元素值。
    (8)Empty(L):判空操作。若L为空表,返回true,否则返回false。
    (9)DestroyList(&L);销毁操作。销毁线性表,并释放线性表L所占用的内存空间。

    注意
    基本操作的实现取决于采用的存储结构,存储结构不同,算法的实现也不同。

  • 线性表的顺序存储结构:静态分配,动态分配

    顺序表

    线性表的顺序存储又称顺序表。它是用一种地址连续的存储单元,依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上用相邻。

    注意
    线性表中的元素的位序是从1开始的,而数组中元素的下标是从0开始的。

    假定线性表的元素类型是ElemType,线性表的顺序存储类型描述为:

    #define MaxSize 50	//定义线性表的最大长度
    typedef struct{
    	ElemType data[MaxSize];	//顺序表的元素
    	int length;	//顺序表的当前长度
    }SqList;	//顺序表的类型定义
    

    静态分配
    大小和空间事先已经固定,一旦空间占满,再加入新的数据将产生溢出,就会导致程序崩溃。

    动态分配
    存储空间是在程序执行过程中通过动态存储分配语句分配的,一旦数据空间占满可以另外开辟一块更大的存储空间,用以替换原来的存储空间,从而达到扩充存储空间的目的,而不需要一次性的划分所有所需空间给线性表。

    动态分配过程
    (1)申请一个更大的空间
    (2)把原来空间的东西复制到新的空间内
    (3)如果新的空间不为空则分配成功,否则分配失败

      #define InitSize 100	//表长度的初始定义
      typedef struct{
      	ElenType *data;	//指示动态分配数组的指针
      	int MaxSize,length;	//数组的最大容量和当前个数
      }SqList;	//动态分配数组顺序表的类型定义
    

    动态分配的函数realloc
    realloc函数的用法

    C语言的初始动态分配语句为

      L.data = (ElemType*)malloc(sizeof(ElemType)*InitSize);
    

    C++的初始动态分配语句为

      L.data = new ElemType[InitSize];
    

    注意
    动态分配并不是链式存储,同样还是属于顺序存储结构,其物理结构没有变化,依然是随机存取方式,只是分配的空间大小可以在运行时决定。
    (1)顺序表最主要的特点是随机访问
    (2)顺序表的存储密度高,每个节点只存储数据元素。
    (3)顺序表逻辑上相邻的元素物理上也相邻,所以插入和删除操作需要移动大量的元素。

  • 顺序表的插入删除算法,移动元素次数分析

    1、插入操作

    在顺序表的第 i (1 ≦ i ≦ L.length+1) 个位置上插入新元素e。

    算法思想:如果输入的i不合法,则返回false,表示插入失败;否则,将顺序表的第 i 个元素以及以后的所以元素右移一个位置,腾出一个空位插入新元素e,顺序表长度增加1,插入成功,返回true。

    注意

    在算法核心为进行之前应检验待运算数据的合法性

      bool ListInsert(SqList &L,int i,ElemType e){
      //本算法实现将元素e插入到顺序表L中的第i个位置。
      	if(i < 1 || i > L.length + 1)	//检验i的合法性。L.length + 1是因为线性表位从1开始。
      		return false;
      	if(L.length > = MaxSize)	//检验线性表的合法性。当前存储空间已满,不能插入
      		return false;
      	for(int j = L.length;j > i;j--)	//将第i个元素及之后的元素后移。注意从尾部开始移动。
      							        //j = L.length是因为数组下标从0开始
      		L.data[j] = L.data[j-1];	//这里以数组为例,注意数组下标和线性表位的区别
      	L.data[i-1] = e;	//在位置i处放入e
      	L.length++;	//线性表长度加1
      	return true;
      		
      }
    

    算法分析

    最好情况:在表尾插入,元素后移语句将不执行,时间复杂度为O(1)。
    最坏情况:在表头插入,元素后移语句将执行n次,时间复杂度为O(n)。
    平均情况:假设Pi是在第i个位置上插入一个节点的概率,则在长度为n的线性表中插入一个节点时所需移动节点的平均次数为

    2、删除操作

    算法思想
    删除顺序表L中第i(1 ≦ i ≦ L.length)个位置的元素,成功则返回true,并将被删除的元素用引用变量e返回,否则返回false。

    bool ListDelete(SqList &L,int i,ElemType &e){
    //本算法实现删除顺序表L中第i个位置的元素
    	if(i < 1 || i > L.length)	//检验i的合法性。
    		return false;
    	e = L.data[i-1];	//将被删除的元素赋给e
    	for(int j = i;j < L.length;i++)
    		L.data[j - 1] = L.data[j];	//将第i个位置之后的元素前移
    	L.length--;	//线性表长度减1
    	return true;
    }
    

    算法分析

    最好情况:删除表尾元素,无须移动元素,时间复杂度为O(1)。
    最坏情况:删除表头元素,需要移动除第一个元素外的所以元素,时间复杂度为O(n)。
    平均情况:假设Pi是删除第i个位置上节点的概率,则在长度为n的线性表中删除一个节点所需移动节点的平均次数为

    因此,线性表删除算法的平均时间复杂度为O(n)。

    3、按值查找(顺序查找)

    算法思想:在顺序表中查找第一个等于e的元素,并返回其位序。

    int LocateElem(SqList L,ElemType e){
    //本算法实现查找顺序表中值为e的元素,如果查找成功,返回元素位序,否则返回0。
    	int i;
    	for(i = 0;i < L.length;i++)
    		if(L.data[i] == e)
    			return i + 1;	//下标为i的元素等于e,则其位序为i+1。
    	return 0;	//退出循环,查找失败。
    }
    

    算法分析:

    最好情况:查找元素就在表头,仅需比较一次,时间复杂度为O(1)。
    最坏情况:查找元素在表尾(或不存在)时,需要比较n次,时间复杂度为O(n)。
    平均情况:假设Pi是查找的元素在第i位置上的概率,则在长度为n的线性表中查找为e的元素所需比较的平均次数为

  • 顺序存储结构的优缺点,引出单链表的结构类型定义

    1、优点
    (1)是一种随机存取的结构,存取任何元素的时间都是一个常数,速度快。
    (2)结构简单,逻辑上相邻的元素物理上也相邻
    (3)不使用指针,节省存储空间
    2、缺点
    (1)插入和删除元素要移动大量元素,消耗大量时间;
    (2)需要一个连续的存储空间;
    (3)插入元素可能发生“溢出”;
    (4)自由区中的存储空间不能被其它数据占用(共享)。

    由于顺序表的插入、删除操作需要移动大量的元素,影响了运行效率,由此引入了线性表的链式存储。
    链式存储线性表时,不需要使用地址连续的存储单元,它是通过链建立起数据元素之间的逻辑关系,因此,对线性表的插入、删除不需要移动元素,而只需要修改指针。

    线性表的链式存储又称单链表,它是通过一组任意的存储单元来存储线性表中的数据元素。
    为了建立起数据元素之间的线性关系,对每个链表节点,除了存放元素自身的信息外,还需要存放一个指向其后继的指针。

    单链表节点类型描述如下:

    typedef struct LNode{	//定义单链表节点类型
    	ElemType data;	//数据域
    	struct LNode *next;	//指针域
    }LNode,*LinkList;
    

    利用单链表可以解决顺序表需要大量的连续存储空间的缺点,但是单链表附加指针域,也存在浪费存储空间的缺点。由于单链表的元素是离散地分布在存储空间中的,所以单链表是非随机存取的存储结构。查找某个节点时需要从表头开始遍历,依次查找。

    通常"头指针"来标识一个单链表,如单链表L,头指针为“NULL”时则表示一个空表。此外,为了操作上的方便,在单链表第一个节点之前附加一个节点,成为 头结点 。头结点的数据域可以不设任何信息,也可以记录表长等相关信息。头结点的指针域指向线性表的第一个元素节点。

    头结点和头指针的区分:

    不管带不带头结点,头指针始终指向链表的第一个节点。而头结点是带头结点链表中的第一个节点,节点内通常不存储信息。

    引入头结点后,可以带来两个优点:
    (1)由于开始节点的位置被存储在头结点的指针域中,所以在链表的第一个位置上的操作和在表的其他位置上的操作一致,无须进行特殊处理。
    (2)无论链表是否为空,其头指针是指向头结点的非空指针(空表中头结点的指针域为空),因此空表和非空表的处理也就统一了。

  • 单链表的算法:生成先进先出单链表,后进先出单链表

    先进先出单链表即尾插法建立单链表。
    后进先出单链表即头插法建立单链表。
    这两种算法属于单链表基本操作,见末尾补充环节。

  • 单链表的算法:生成不带表头的递增有序单链表,生成带表头的递增有序单链表

    算法1:生成不带表头的递增有序单链表
    算法如下:

//
//  main.cpp
//  dataStruct
//
//  Created by 海岩 on 2019/9/13.
//  Copyright © 2019 海岩. All rights reserved.
//

#include <iostream>
using namespace std;
typedef struct LNode{
    int data;
    struct LNode *next;
}LNode,*LinkList;

LinkList Create_EndList();
bool display_List(LinkList L);
LinkList insert_up(LinkList L,LNode *s);
int lengthList(LinkList L);

int main(int argc, const char * argv[]) {
    LinkList L;
    
    L = Create_EndList();
    if(display_List(L))
        cout<<"该表为空,无法输出。"<<'\n';
    cout<<"表长为:"<<lengthList(L)<<'\n';
    cout<<'\n';
    return 0;
}
LinkList insert_up(LinkList L,LNode *s){
    
    LNode *p = L,*r = L->next;
    if(r == NULL&&p->data == 999){
        p->data = s->data;
        return L;
    }
    while(p){
        if(s->data >= p->data){
            if(r == NULL || s->data < r->data)
            {
                s->next = r;
                p->next = s;
                return L;
            }
        }
        p = p->next;
        r = r->next;
    }
    return L;
}
LinkList Create_EndList(){
    
    LinkList L = new LNode;
    L->data = 999;
    L->next = NULL;
    LNode *s;
    int data;
    cout<<"输入整数节点值(输入999代表结束输入):";
    cin>>data;
    
    while(data!=999){
        s = new LNode;
        s->data = data;
        s->next = NULL;
        L = insert_up(L,s);
        cout<<"输入整数节点值(输入999代表结束输入):";
        cin>>data;
    }
    
    return L;
}

bool display_List(LinkList L){
    
    LNode *cheak = L;
    while(cheak!=NULL){
        cout<<cheak->data<<"->";
        cheak = cheak->next;
    }
    cout<<'\n';
    return false;
    
}

int lengthList(LinkList L){
    
    int i = 0;
    LNode *p = L;
    while(p){
        i++;
        p = p->next;
    }
    return i;
}

运行结果:

算法2:生成带表头的递增有序单链表 算法如下:
//
//  main.cpp
//  dataStruct
//
//  Created by 海岩 on 2019/9/13.
//  Copyright © 2019 海岩. All rights reserved.
//

#include <iostream>
using namespace std;
typedef struct LNode{
    int data;
    struct LNode *next;
}LNode,*LinkList;

LinkList Create_EndList();
bool display_List(LinkList L);
LinkList insert_up(LinkList L,LNode *s);
int lengthList(LinkList L);

int main(int argc, const char * argv[]) {
    LinkList L;
    
    L = Create_EndList();
    if(display_List(L))
        cout<<"该表为空,无法输出。"<<'\n';
    cout<<"表长为:"<<lengthList(L)<<'\n';
    cout<<'\n';
    return 0;
}
LinkList insert_up(LinkList L,LNode *s){
    
    int i=1;    //设置i是为了识别新来的节点小于链表的第一个节点值时
    LNode *p = L->next;
    LNode *r = NULL;
    if(p == NULL){
        L->next = s;
        return L;
    }
    while(p){
        
        r = p->next;
        if(s->data >= p->data){
            if(r == NULL || s->data < r->data)
            {
                s->next = r;
                p->next = s;
                return L;
            }
        }
        else{			//如果新来的节点小于第一个节点值,就用头插法,把它查到第一个位置
            if(i==1){
                s->next = p;
                L->next = s;
                return L;
            }
        }
        p = p->next;
        i++;
    }
    return L;
}
LinkList Create_EndList(){
    
    LinkList L = new LNode;
    L->next = NULL;
    LNode *s;
    int data;
    cout<<"输入整数节点值(输入999代表结束输入):";
    cin>>data;
    
    while(data!=999){
        s = new LNode;
        s->data = data;
        s->next = NULL;
        L = insert_up(L,s);
        cout<<"输入整数节点值(输入999代表结束输入):";
        cin>>data;
    }
    
    return L;
}

bool display_List(LinkList L){
    
    LNode *cheak = L->next;
    if(cheak == NULL)
         return true;   // 如果链表为空,在main函数内给出提示。
    while(cheak!=NULL){
        cout<<cheak->data<<"->";
        cheak = cheak->next;
    }
    cout<<'\n';
    return false;
    
}

int lengthList(LinkList L){
    
    int i = 0;
    LNode *p = L->next;
    while(p){
        i++;
        p = p->next;
    }
    return i;
}

运行结果:

  • 单链表的算法:在指定位置插入一个新结点;删除指定值的结点;在指定位置删除一个结点;

    插入和删除算法属于单链表基本操作,见末尾补充环节。
  • 单链表的合并:两个递增有序的单链表合并成一个递增有序的单链表

算法思想:选取其中一个链表1不变,从第一个节点依次取出链表2的节点,把每个节点按递增次序插入到链表1中,得到新的合并链表1。
注意:此算法还可以改进,因为两个链表事先都是递增的,那么每次插入一个节点,同时记录插入的位置,那么下次再插入节点时,只需要从上一个插入位置之后开始插入就可以了。
算法如下:

//
//  main.cpp
//  dataStruct
//
//  Created by 海岩 on 2019/9/13.
//  Copyright © 2019 海岩. All rights reserved.
//

#include <iostream>
using namespace std;
typedef struct LNode{
    int data;
    struct LNode *next;
}LNode,*LinkList;

LinkList Create_up_EndList();
bool display_List(LinkList L);
LinkList insert_up(LinkList L,LNode *s);
int lengthList(LinkList L);
LinkList sort_up(LinkList L1,LinkList L2);

int main(int argc, const char * argv[]) {
    LinkList L1=NULL,L2=NULL,L;
    
    cout<<"创建第1个递增链表:"<<'\n';
    
    L1 = Create_up_EndList();
    if(display_List(L1))
        cout<<"该表为空,无法输出。"<<'\n';
    cout<<"表长为:"<<lengthList(L1)<<'\n';
    
    cout<<"创建第2个递增链表:"<<'\n';
    L2 = Create_up_EndList();
    if(display_List(L2))
        cout<<"该表为空,无法输出。"<<'\n';
    cout<<"表长为:"<<lengthList(L2)<<'\n';
    
    L = sort_up(L1,L2);
    
    if(display_List(L))
        cout<<"该表为空,无法输出。"<<'\n';
    cout<<"表长为:"<<lengthList(L)<<'\n';
    cout<<'\n';
    return 0;
}
LinkList insert_up(LinkList L,LNode *s){
    
    int i=1;
    LNode *p = L->next;
    LNode *r = NULL;
    if(p == NULL){
        L->next = s;
        return L;
    }
    while(p){
        
        r = p->next;
        if(s->data >= p->data){
            if(r == NULL || s->data < r->data)
            {
                s->next = r;
                p->next = s;
                return L;
            }
        }
        else{
            if(i==1){
                s->next = p;
                L->next = s;
                return L;
            }
        }
        p = p->next;
        i++;
    }
    return L;
}
LinkList Create_up_EndList(){
    
    LinkList L = new LNode;
    L->next = NULL;
    LNode *s;
    int data;
    cout<<"输入整数节点值(输入999代表结束输入):";
    cin>>data;
    
    while(data!=999){
        s = new LNode;
        s->data = data;
        s->next = NULL;
        L = insert_up(L,s);
        cout<<"输入整数节点值(输入999代表结束输入):";
        cin>>data;
    }
    
    return L;
}

bool display_List(LinkList L){
    
    LNode *cheak = L->next;
    if(cheak == NULL)
        return true;   // 如果链表为空,在main函数内给出提示。
    while(cheak!=NULL){
        cout<<cheak->data<<"->";
        cheak = cheak->next;
    }
    cout<<'\n';
    return false;
    
}

LinkList sort_up(LinkList L1,LinkList L2){
    
    LNode *p = L2->next;
    LNode *s;
    while(p){
        
        s = new LNode;
        s->data = p->data;
        s->next = NULL;
        L1 = insert_up(L1,s);
        p = p->next;
    }
    return L1;
    
}



int lengthList(LinkList L){
    
    int i = 0;
    LNode *p = L->next;
    while(p){
        i++;
        p = p->next;
    }
    return i;
}

运行结果:

第二种算法
输入:两单链表的头指针
输出:合并后单链表的头指针
在这里只写出函数,可以画出示意图仔细体会此算法的思想。

	LinkList sort_up(LinkList L1,LinkList L2){

	LNode *pa,*pb,*pc;
	pa=La->next;	//pa指向表La的首结点
	pb=Lb->next;	//pb指向表Lb的首结点
	pc=La; 		//使用表La的头结点,pc为尾指针
	free(Lb);		//释放表Lb的头结点
	while(pa&&pb){	//表a表b中均有节点
		if (pa->data<=pb->data) {	//取表La的一个结点
			pc->next=pa; 			//插在表Lc的尾结点之后
		 	pc=pa; 				//变为表Lc新的尾结点
	 		pa=pa->next; 			//移向表La下一个结点
		}
		else {						//取表Lb的一个结点
			pc->next=pb; 			//插在表Lc的尾结点之后
			pc=pb; 				//变为表Lc新的尾结点
			pb=pb->next; 			//移向表Lb下一个结点
		 } 
	}
	if (pa) pc->next=pa; 			//插入表La的剩余段
	else pc->next=pb; 				//插入表Lb的剩余段
	return La; 
}
  • 循环链表的概念,双向循环链表的概念,插入和删除结点

循环单链表:
循环单链表和单链表的区别在于,表中最后一个节点的指针不是NULL,而改为指向头节点,从而整个链表形成一个环。

因此,循环单链表的判空条件不是头节点的指针是否为空,而是它的指针是否等于头指针。

循环单链表的插入、删除算法与单链表几乎一样,不同的是如果操作在表尾进行,则执行的操作不同,但也仅仅修改了指针而已,以至于保持循环的特性。

在单链表中只能从表头节点开始往后遍历整个表,而循环链表可以从表的任何节点开始遍历整个表。

注意:

有时对单链表常做的操作在表头和表尾进行的,此时可对循环链表不设头指针而仅设尾指针,从而使得操作效率更高。其原因是若设的是头指针,对表尾进行操作需要O(n)的时间复杂度,而如果设的是表尾指针,对于表头与表尾的操作都只需要O(1)的时间复杂度。

循环双链表:

由循环单链表的定义不难推出循环双链表,不同的是在循环双链表中,头节点的prior指针还要指向表尾节点。
在循环双链表L中,某节点*p为表尾节点时,p->next==L;当循环双链表为空表时,其头节点的prior指针和next指针都指向L。

在此只对循环双链表的插入、删除写出算法。

(1)插入节点

双向链表只是比单链表多了一个前驱指针,循环双向链表只是把双链表尾部的后继指针由NULL改为头而已,算法大致没有差别,只有关键代码有区别而已。这里只写出关键代码。

双链表中节点类型描述如下:

typedef struct DNode{
	ElemType data;
	struct DNode *prior,*next;
}DNode,*DLinklist;

插入操作:
在双链表中p所指的节点之后插入节点*s,其指针变化过程如下代码:

(1)s->next = p->next;
(2)p->next->prior = s;
(3)s->pripr = p;
(4)p->next = s;

插入节点应注意:
p的后继指针不能丢,要用s连接p的后继。
(2)删除节点
删除双链表中节点 *p的后继节点 *q,其指针变化过程如下代码:

p->next = q->next;
q->next->prior = p;
free(q);
  • 多项式的链表表示,算法思想

    这里只要求算法思想,那我就只写出算法思想,如有需要可自行按思想写出算法。
    以一元多项式为例,未知变量为x。
    分析1:
    多项式由什么组成?答:由多项式的每个项组成。
    分析2:
    每个项由什么组成?答:符号、系数、次幂
    分析3:
    如何存放这些项?答:建立结构体。

    typedef struct LNode{
      char sign;	//存放符号:+,-
      int data;		//存放系数,这里以整数为例
      int time;		//存放次幂
      struct DNode *next;
    }LNode,*Linklist;
    

通过以上分析,得出了多项式的每一项的节点表示。
那么就可以把每一项以节点的形式连接在链表中了。

算法详解:

补充

  • 单链表基本操作:

  • (1)头插法建立单链表

    算法思想:

    该方法从一个空表开始,生成新的节点,并将读取到的数据存放在新节点的数据域中,然后将新节点插入到当前链表的表头,即头节点之后。读者可以画出头插法示意图,以便于理解。

    算法如下:

      //
      //  main.cpp
      //  dataStruct
      //
      //  Created by 海岩 on 2019/9/13.
      //  Copyright © 2019 海岩. All rights reserved.
      //
    
      #include <iostream>
      using namespace std;
      typedef struct LNode{
      	int data;
      	struct LNode *next;
      }LNode,*LinkList;
    
      LinkList Create_HeadList();
      bool display_List(LinkList L);
    
      int main(int argc, const char * argv[]) {
      	LinkList L;
      	L = Create_HeadList();
      	if(display_List(L))
      		cout<<"该表为空,无法输出。";
      	cout<<'\n';
      	return 0;
      }
      LinkList Create_HeadList(){
      	//头插法创建单链表
      	LinkList L = new LNode;
      	L->next = NULL;
      	LNode *s;
      	int data;
      	cout<<"输入整数节点值(输入999代表结束输入):";
      	cin>>data;
      	while(data!=999){
      		s = new LNode;
      		s->data = data;
      		s->next = L->next;  //建立单链表的关键在于如何连接节点
      		L->next = s;        //注意画出示意图,以防出错
      		cout<<"输入整数节点值(输入999代表结束输入):";
      		cin>>data;
      	}
      	return L;
      }
    
      bool display_List(LinkList L){
      	LNode *cheak = L->next;
      	if(cheak == NULL)
       		return true;   // 如果链表为空,在main函数内给出提示。
      	while(cheak!=NULL){
      		cout<<cheak->data<<"->";
      		cheak = cheak->next;
      	}
      	return false;
      }
    

    运行结果:

  • (2)尾插法建立单链表

    算法思想:
    头插法虽然简单,但生成的链表中节点的次序跟输入次序不一致。若希望次序一致,可采用尾插法。
    该方法是将新节点插入到当前链表的表尾上,为此必须增加一个尾指针r,使其始终指向当前链表的表尾。

    算法如下:

      //
      //  main.cpp
      //  dataStruct
      //
      //  Created by 海岩 on 2019/9/13.
      //  Copyright © 2019 海岩. All rights reserved.
      //
    
      #include <iostream>
      using namespace std;
      typedef struct LNode{
      	int data;
      	struct LNode *next;
      }LNode,*LinkList;
    
      LinkList Create_EndList();
      bool display_List(LinkList L);
    
      int main(int argc, const char * argv[]) {
      LinkList L;
      	L = Create_EndList();
      	if(display_List(L))
      		cout<<"该表为空,无法输出。";
    
      	cout<<'\n';
      	return 0;
      }
      LinkList Create_EndList(){
      	//尾插法创建单链表
      	LinkList L = new LNode;
      	L->next = NULL;
      	LNode *s,*r; //r指针用来标记表尾,r始终指向表尾节点
      	r = L;
      	int data;
      	cout<<"输入整数节点值(输入999代表结束输入):";
      	cin>>data;
      	while(data!=999){
      		s = new LNode;
      		s->data = data;
      		r->next = s;
      		r = s;  //r再次指向新的表尾节点
      		cout<<"输入整数节点值(输入999代表结束输入):";
      		cin>>data;
      	}
      	r->next = NULL;
      	return L;
      }
    
      bool display_List(LinkList L){
    
      	LNode *cheak = L->next;
      	if(cheak == NULL)
       		return true;   // 如果链表为空,在main函数内给出提示。
      	while(cheak!=NULL){
      		cout<<cheak->data<<"->";
      		cheak = cheak->next;
      	}
      	return false;
      	}
    

    运行结果:

  • (3)按序号查询节点值

    算法思想:
    在单链表中从第一个节点出发,顺指针next域逐个往下搜索,直到找到第i个节点为止,否则返回最后一个节点指针域NULL。
    算法如下:

      //
      //  main.cpp
      //  dataStruct
      //
      //  Created by 海岩 on 2019/9/13.
      //  Copyright © 2019 海岩. All rights reserved.
      //
    
      #include <iostream>
      using namespace std;
      typedef struct LNode{
      	int data;
      	struct LNode *next;
      }LNode,*LinkList;
    
      LinkList Create_EndList();
      bool display_List(LinkList L);
      bool seek_index(LinkList L,int index);
    
      int main(int argc, const char * argv[]) {
      	LinkList L;
      	int index;
      	L = Create_EndList();
      	if(display_List(L))
      		cout<<"该表为空,无法输出。"<<'\n';
      	cout<<"输入查询的位置:";
      	cin>>index;
      	if(seek_index(L,index))
      		cout<<"查询出错。";
      	cout<<'\n';
      	return 0;
      }
      LinkList Create_EndList(){
      	//尾插法创建单链表
      	LinkList L = new LNode;
      	L->next = NULL;
      	LNode *s,*r; //r指针用来标记表尾,r始终指向表尾节点
      	r = L;
      	int data;
      	cout<<"输入整数节点值(输入999代表结束输入):";
      	cin>>data;
      	while(data!=999){
      		s = new LNode;
      		s->data = data;
      		r->next = s;
      		r = s;  //r再次指向新的表尾节点
      		cout<<"输入整数节点值(输入999代表结束输入):";
      		cin>>data;
      	}
      	r->next = NULL;
      	return L;
      }
    
      bool display_List(LinkList L){
    
      	LNode *cheak = L->next;
      	if(cheak == NULL)
       		return true;   // 如果链表为空,在main函数内给出提示。
      	while(cheak!=NULL){
      		cout<<cheak->data<<"->";
      		cheak = cheak->next;
      	}
      	cout<<'\n';
      	return false;
    
      }
    
      bool seek_index(LinkList L,int index){
    
      	int i=1;
      	LNode *cheak = L->next;
      	if(cheak == NULL){
      		cout<<"表为空。"<<'\n';
      		return true;
      	}
      	if(index == 0){
      		cout<<"你要查询的位置为头指针,没有数据。"<<'\n';
      		return true;
      	}
      	if(index < 0){
      		cout<<"你要查询的位置为负,此位置不存在。"<<'\n';
      		return true;
      	}
    
      	while(cheak&&i<index){
      		cheak = cheak->next;
      		i++;
      	}
      	if(cheak == NULL){
      		cout<<"查找位置超出表长,此位置不存在"<<'\n';
      		return true;
      	}
      	cout<<"查找成功:"<<index<<"----"<<cheak->data<<'\n';
      	return false;
      }
    

    运行结果:

总结:
其实算法实现所需功能并不难,难点在于如何成为一个好的算法。编写算法要注意思考:算法的正确性、可读性、健壮性、优化性。
就如本算法,你会发现有大量的if语句,其实这些都是在预防可能是程序崩溃的情况发生。
(1)表为空时,查找会出错。
(2)查找为位置为负,查找会出错。
(3)查找位置为零,查找会出错。
(4)查找位置超出表长,查找会出错。
除了要发现这些错误,还要给出错误的原因,这才有可能将初步算法优化为一个好的算法。

  • (4)按值查询表节点

    算法思想:
    从单链表的第一个节点开始,由前往后依次比较表中各节点数据域的值,若节点数据域的值等于给定的值data,则返回该节点的位置,若整个表中无这样的节点,则返回出错信息。

    算法如下:

      //
      //  main.cpp
      //  dataStruct
      //
      //  Created by 海岩 on 2019/9/13.
      //  Copyright © 2019 海岩. All rights reserved.
      //
    
      #include <iostream>
      using namespace std;
      typedef struct LNode{
      	int data;
      	struct LNode *next;
      }LNode,*LinkList;
    
      LinkList Create_EndList();
      bool display_List(LinkList L);
      bool seek_data(LinkList L,int data);
    
      int main(int argc, const char * argv[]) {
      	LinkList L;
      	int data;
      	L = Create_EndList();
      	if(display_List(L))
     		cout<<"该表为空,无法输出。"<<'\n';
      	cout<<"输入查询的节点值:";
      	cin>>data;
      	if(seek_data(L,data))
      			cout<<"查询出错。";
      		cout<<'\n';
      	return 0;
      }
      LinkList Create_EndList(){
      	//尾插法创建单链表
      	LinkList L = new LNode;
      	L->next = NULL;
      	LNode *s,*r; //r指针用来标记表尾,r始终指向表尾节点
      	r = L;
      	int data;
      	cout<<"输入整数节点值(输入999代表结束输入):";
      	cin>>data;
      	while(data!=999){
      		s = new LNode;
      		s->data = data;
      		r->next = s;
      		r = s;  //r再次指向新的表尾节点
      		cout<<"输入整数节点值(输入999代表结束输入):";
      		cin>>data;
      	}
      	r->next = NULL;
      	return L;
      }
    
      bool display_List(LinkList L){
    
      	LNode *cheak = L->next;
      	if(cheak == NULL)
       		return true;   // 如果链表为空,在main函数内给出提示。
      	while(cheak!=NULL){
      		cout<<cheak->data<<"->";
      		cheak = cheak->next;
      }
      	cout<<'\n';
      	return false;
    
      }
    
      bool seek_data(LinkList L,int data){
    
      	int i=1;
      	LNode *cheak = L->next;
      	if(cheak == NULL){
      		cout<<"表为空。"<<'\n';
      		return true;
      	}
    
      	while(cheak&&cheak->data!=data){
      		cheak = cheak->next;
      		i++;
      	}
      	if(cheak == NULL){
      		cout<<"查找的值不存在"<<'\n';
      		return true;
      	}
      	cout<<"查找成功:"<<cheak->data<<"--对应的位置为--"<<i<<'\n';
      	return false;
      }
    

    运行结果:

  • (5)插入节点操作

    算法思想:
    插入操作是将值为x的新节点插入到单链表的第i各位置上。先检查插入位置的合法性,然后找到待插入位置的前驱节点,即第i-1各节点,再在其后插入新节点。

    算法如下:

//
//  main.cpp
//  dataStruct
//
//  Created by 海岩 on 2019/9/13.
//  Copyright © 2019 海岩. All rights reserved.
//

#include <iostream>
using namespace std;
typedef struct LNode{
    int data;
    struct LNode *next;
}LNode,*LinkList;

LinkList Create_EndList();
bool display_List(LinkList L);
LNode *GetElem(LinkList L,int index);
bool insert_index(LinkList L,int index,int data);

int main(int argc, const char * argv[]) {
    LinkList L;
    int index;
    int data;
    L = Create_EndList();
    if(display_List(L))
        cout<<"该表为空,无法输出。"<<'\n';
    cout<<"输入要插入的节点位置:";
    cin>>index;
    cout<<"输入要插入的节点值:";
    cin>>data;
    if(insert_index(L,index,data))
        cout<<"插入出错。";
    cout<<'\n';
    return 0;
}
LinkList Create_EndList(){
    //尾插法创建单链表
    LinkList L = new LNode;
    L->next = NULL;
    LNode *s,*r; //r指针用来标记表尾,r始终指向表尾节点
    r = L;
    int data;
    cout<<"输入整数节点值(输入999代表结束输入):";
    cin>>data;
    while(data!=999){
        s = new LNode;
        s->data = data;
        r->next = s;
        r = s;  //r再次指向新的表尾节点
        cout<<"输入整数节点值(输入999代表结束输入):";
        cin>>data;
    }
    r->next = NULL;
    return L;
}

bool display_List(LinkList L){
    
    LNode *cheak = L->next;
    if(cheak == NULL)
        return true;   // 如果链表为空,在main函数内给出提示。
    while(cheak!=NULL){
        cout<<cheak->data<<"->";
        cheak = cheak->next;
    }
    cout<<'\n';
    return false;
}

LNode *GetELem(LinkList L,int index){
    int i=0;
    LNode *cheak = L;
    
    if(index < 0){
        cout<<"位置序号从1开始,位置不合法"<<'\n';
        return NULL;
    }
    while(cheak&&i<index){
        cheak = cheak->next;
        i++;
    }
    if(cheak == NULL){
        cout<<"插入位序过大,位置不合法"<<'\n';
        return NULL;
    }
    return cheak;
    
}

bool insert_index(LinkList L,int index,int data){
    
    LNode *p;
    LNode *s = new LNode;
    s->data = data;
    p = GetELem(L, index-1);
    if(p == NULL)
        return true;
    s->next = p->next;
    p->next = s;
    cout<<"插入成功"<<'\n';
    display_List(L);
    return false;
    }

运行结果:

  • (6)指定位置删除节点操作

算法思想:
删除操作是将单链表的第i各节点删除。先检查删除位置的合法性,然后查找表中的第i-1个节点,即被删除节点的前驱节点,再将其删除。并说明删除的值。

算法如下:

//
//  main.cpp
//  dataStruct
//
//  Created by 海岩 on 2019/9/13.
//  Copyright © 2019 海岩. All rights reserved.
//

#include <iostream>
using namespace std;
typedef struct LNode{
    int data;
    struct LNode *next;
}LNode,*LinkList;

LinkList Create_EndList();
bool display_List(LinkList L);
LNode *GetElem(LinkList L,int index);
bool del_index(LinkList L,int index);

int main(int argc, const char * argv[]) {
    LinkList L;
    int index;
    L = Create_EndList();
    if(display_List(L))
        cout<<"该表为空,无法输出。"<<'\n';
    cout<<"输入要删除的节点位置:";
    cin>>index;
    if(del_index(L,index))
        cout<<"删除出错。";
    cout<<'\n';
    return 0;
}
LinkList Create_EndList(){
    //尾插法创建单链表
    LinkList L = new LNode;
    L->next = NULL;
    LNode *s,*r; //r指针用来标记表尾,r始终指向表尾节点
    r = L;
    int data;
    cout<<"输入整数节点值(输入999代表结束输入):";
    cin>>data;
    while(data!=999){
        s = new LNode;
        s->data = data;
        r->next = s;
        r = s;  //r再次指向新的表尾节点
        cout<<"输入整数节点值(输入999代表结束输入):";
        cin>>data;
    }
    r->next = NULL;
    return L;
}

bool display_List(LinkList L){
    
    LNode *cheak = L->next;
    if(cheak == NULL)
        return true;   // 如果链表为空,在main函数内给出提示。
    while(cheak!=NULL){
        cout<<cheak->data<<"->";
        cheak = cheak->next;
    }
    cout<<'\n';
    return false;
    
}

LNode *GetELem(LinkList L,int index){
    int i=0;
    LNode *cheak = L;
    if(index < 0){
        cout<<"位置序号从1开始,位置不合法"<<'\n';
        return NULL;
    }
    while(cheak&&i<index){
        cheak = cheak->next;
        i++;
    }
    if(cheak->next == NULL){
        cout<<"删除位序过大,位置不合法"<<'\n';    //注意这里如何判断位序过大,和插入位序不同之处
        return NULL;
    }
    return cheak;
    
}

bool del_index(LinkList L,int index){
    
    int data;
    LNode *p;
    LNode *s;
    p = GetELem(L, index-1);
    if(p == NULL)
        return true;
    s = p->next;
    p->next = s->next;
    data = s->data;
    free(s);
    cout<<"删除成功"<<'\n';
    cout<<"在"<<index<<"位置,删除了"<<data<<'\n';
    display_List(L);
    return false;
    }

运行结果:

  • (7)指定值删除节点操作

算法思想:
给定一个节点值,对单链表逐个对比,找到这个值,同时再找到它的前驱节点,然后再删除它。如果找不到改值,则说名出错信息。

算法如下:

//
//  main.cpp
//  dataStruct
//
//  Created by 海岩 on 2019/9/13.
//  Copyright © 2019 海岩. All rights reserved.
//

#include <iostream>
using namespace std;
typedef struct LNode{
    int data;
    struct LNode *next;
}LNode,*LinkList;

LinkList Create_EndList();
bool display_List(LinkList L);
LNode *GetElem(LinkList L,int data);
bool del_index(LinkList L,int index);

int main(int argc, const char * argv[]) {
    LinkList L;
    int data;
    L = Create_EndList();
    if(display_List(L))
        cout<<"该表为空,无法输出。"<<'\n';
    cout<<"输入要删除的节点值:";
    cin>>data;
    if(del_index(L,data))
        cout<<"删除出错。";
    cout<<'\n';
    return 0;
}
LinkList Create_EndList(){
    //尾插法创建单链表
    LinkList L = new LNode;
    L->next = NULL;
    LNode *s,*r; //r指针用来标记表尾,r始终指向表尾节点
    r = L;
    int data;
    cout<<"输入整数节点值(输入999代表结束输入):";
    cin>>data;
    while(data!=999){
        s = new LNode;
        s->data = data;
        r->next = s;
        r = s;  //r再次指向新的表尾节点
        cout<<"输入整数节点值(输入999代表结束输入):";
        cin>>data;
    }
    r->next = NULL;
    return L;
}

bool display_List(LinkList L){
    
    LNode *cheak = L->next;
    if(cheak == NULL)
         return true;   // 如果链表为空,在main函数内给出提示。
    while(cheak!=NULL){
        cout<<cheak->data<<"->";
        cheak = cheak->next;
    }
    cout<<'\n';
    return false;
    
}

LNode *GetELem(LinkList L,int data){
    
    LNode *cheak = L->next;
    LNode *cheak0 = L;
    if(cheak == NULL){
        cout<<"表为空。"<<'\n';
        return NULL;
    }
    while(cheak&&cheak->data!=data){
        cheak = cheak->next;
        cheak0 = cheak0->next;
    }
    if(cheak->next == NULL){
        cout<<"删除的值不存在。"<<'\n';
        return NULL;
    }
    return cheak0;
    
}

bool del_index(LinkList L,int data){
    
    LNode *p;
    LNode *s;
    p = GetELem(L, data);
    if(p == NULL)
        return true;
    s = p->next;
    p->next = s->next;
    free(s);
    cout<<"删除成功"<<'\n';
    display_List(L);
    return false;
}

运行结果:

  • (8)求表长操作

算法思想:
遍历所有节点,并计数。

算法如下:

//
//  main.cpp
//  dataStruct
//
//  Created by 海岩 on 2019/9/13.
//  Copyright © 2019 海岩. All rights reserved.
//

#include <iostream>
using namespace std;
typedef struct LNode{
    int data;
    struct LNode *next;
}LNode,*LinkList;

LinkList Create_EndList();
bool display_List(LinkList L);
int lengthList(LinkList L);

int main(int argc, const char * argv[]) {
    LinkList L;
    
    L = Create_EndList();
    if(display_List(L))
        cout<<"该表为空,无法输出。"<<'\n';
    cout<<"表长为:"<<lengthList(L)<<'\n';
    cout<<'\n';
    return 0;
}
LinkList Create_EndList(){
    //尾插法创建单链表
    LinkList L = new LNode;
    L->next = NULL;
    LNode *s,*r; //r指针用来标记表尾,r始终指向表尾节点
    r = L;
    int data;
    cout<<"输入整数节点值(输入999代表结束输入):";
    cin>>data;
    while(data!=999){
        s = new LNode;
        s->data = data;
        r->next = s;
        r = s;  //r再次指向新的表尾节点
        cout<<"输入整数节点值(输入999代表结束输入):";
        cin>>data;
    }
    r->next = NULL;
    return L;
}

bool display_List(LinkList L){
    
    LNode *cheak = L->next;
    if(cheak == NULL)
         return true;   // 如果链表为空,在main函数内给出提示。
    while(cheak!=NULL){
        cout<<cheak->data<<"->";
        cheak = cheak->next;
    }
    cout<<'\n';
    return false;
    
}

int lengthList(LinkList L){
    
    int i = 0;
    LNode *p = L->next;
    while(p){
        i++;
        p = p->next;
    }
    return i;
}

运行结果:

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值