0
点赞
收藏
分享

微信扫一扫

408数据结构学习笔记——线性表的链式表示

拾杨梅记 2022-03-23 阅读 58

1.单链表

1.1.单链表的定义

  1. 优点:离散存放(不需要连续的存储空间),改变容量方便
  2. 缺点:不支持随机存储,需要额外空间存放指针
typedef struct LNode{    //定义单链表节点类型
    ElemType data;    //每个节点存放一个数据元素
    struct LNode *next;    //指针指向下个节点
}LNode, *linkList;

要表示单链表的时候,只需申明一个头指针L,指向单链表的第一个节点

//*Lnode L;    //强调是一个节点
linkList L;    //强调是一个链表,代码可读性更强

1.2.不带头结点的单链表

typedef struct LNode {    //定义单链表节点类型
	Elemtype data;    //每个节点存放一个数据元素
	struct LNode *next;    //指针指向下一个节点
}LNode, *linkList;
//初始化单链表
bool initList(linkList &L) {
	//将表置空,防止脏数据
	L = NULL;
	return true;
}
//判断表空
bool empty(linkList L){
    if (L == NULL) return true;
    else return false;
}
void main() {
	//申明一个指向单链表的指针
	linkList L;
	//初始化单链表
	initList(L);
	//后续代码
}

        

1.3.带头结点的单链表

typedef struct LNode {
	Elemtype data;
	struct LNode *next;
}LNode, *linkList;
bool initList(linkList& L) {
	//分配一个头结点
	L = (LNode*)malloc(sizeof(LNode));
	//内存不足,分配失败
	if (L == NULL) return false;
	//当前表空,因此将头结点的next指针置空
	L->next = NULL;
	return false;
}
//判断表空
bool empty(linkList L){
    if (L->next == NULL) return true;
    else return false;
}
void main() {
	//申明一个指向单链表的指针
	linkList L;
	//初始化单链表
	initList(L);
	//后续操作
}

1.4.单链表的插入

1.4.1. 带头结点

bool listInsert(linkList& L, Elemtype e, int i) {
	//判断i值是否合法(判断是否过小)
	if (i < 1) return false;
	//申明一个LNode类型的指针*p,并使其指向linkList的头结点L
	LNode *p = L;
	//遍历单链表,找到要插入的位序
	for (int j = 0; p != NULL && j < i - 1; j++) p = p->next;
	//判断i值是否合法(判断是否过大)
	if (p == NULL) return false;
	//申明一个节点s,并为它分配内存空间和数据
	LNode* s = (LNode*)malloc(sizeof(LNode));
	s->data = e;
	//在单链表中插入s
	s->next = p->next;
	p->next = s;
	return true;
}

1.4.2.不带头结点

bool listInsert(linkList& L, int e, int i) {
	//判断i值是否合法
	if (i < 1) return false;
	//i=1时为特殊情况,在表头插入元素
	if (i == 1) {
		//申明一个节点s,并为其分配空间和输入数据
		LNode* s = (LNode*)malloc(sizeof(LNode));
		s->data = e;
		//将s插入表头,并且把L的指向改为s
		s->next = L;
		L = s;
		return true;
	}
	//申明一个LNode*类型的指针p指向第一个节点,并使用其遍历单链表
	LNode *p = L;
	for (int j = 1; p != NULL && j < i - 1) p = p->next;
	//i值不合法
	if (p == NULL) return false;
	//申明一个节点s,并为其分配空间和输入数据
	LNode* s = (LNode*)malloc(sizeof(LNode));
	s->data = e;
	//将s插入单链表中
	s->next = p->next;
	p->next = s;
	return true;
}

1.4.3.尾插法

//尾插法
bool insertNextNode(LNode *p, int e) {
	//i值不合法
	if (p == NULL) return false;
	LNode *s = (LNode*)malloc(sizeof(LNode));
	//内存分配失败
	if (s == NULL) return false;
	s->data = e;
	//插入s
	s->next = p->next;
	p->next = s;
	return true;
}
bool listInsert(linkList& L, int e, int i) {
	if (i < 1) return false;
	LNode *p = L;
	for (int j = 0; p != NULL && j < i - 1) p = p->next;
	//调用尾插法函数插入元素
	return insertNextNode(p, e);
}

1.4.4.前插法

//前插法
bool insertPriorNode(LNode *p, int e) {
	//i值不合法
	if (p == NULL) return false;
	LNode* s = (LNode*)malloc(sizeof(LNode));
	//在p后插入一个新节点s
	s->next = p->next;
	p->next = s;
	//将p的数据赋值给s,再将e赋值给p
	s->data = p->data;
	p->data = e;
	return true;
}

1.5.单链表的删除

1.5.1.按位序删除(带头结点)

//按位序删除
bool listDelete(linkList& L, int i, Elemtype& e) {
	//i值不合法
	if (i < 1) return false;
	//申明一个LNode类型的指针指向头结点
	LNode* p = L;
	//将指针指向第i - 1个元素
	for (int j = 0; p != NULL && j < i - 1; j++) p = p->next;
	//i值不合法
	if (p == NULL) return false;
	//第i个元素为空
	if (p->next == NULL) return false;
	//取回第i个元素的值
	LNode* q = p->next;
	e = q->data;
	p->next = q->next;
	free(q);
	return true;
}

        

1.5.2.指定节点删除

//指定节点删除
bool deleteNode(LNode* p) {
	//i值不合法
	if (p == NULL) return false;
	//申明一个节点q,并使其指向p->
	LNode* q = p->next;
	//将q的数据赋值给q
	p->data = q->data;
	//q的nxet指针赋值给q
	p->netxt = q->next;
	//释放q
	free(q);
	return true;
}

1.6.单链表按值查找

//按值查找
LNode* locateElem(linkList L, Elemtype e) {
	LNode* p = L->next;
	//从第一个元素遍历查找值为e的结点
	for (int i = 1; p != NULL && p->data != e) p = p->next;
	return p;	//找到后返回该节点指针,否则返回NULL
}

1.7.求单链表长

//求表长
int length(linkList L) {
	//申明一个LNode*类型的指针p,使其指向表中第一个节点
	LNode* p = L->next;
	//标记长度
	int length = 0;
	//遍历单链表
	while (p != NULL) {
		p = p->next;
		length++;
	}
	return length;
}

1.8.单链表的建立

1.8.1.尾插法

linkList listTailInsert(linkList& L) {
	Elemtype x;
	//为单链表L开辟一个新的内存空间,作为头结点
	L = (linkList)malloc(sizeof(linkList));
	//申明两个指针,并让它们指向头结点
	LNode* i = L, * j = L;
	//输入数据
	cin >> x;
	//停止输入(0可以改为任意值)
	while (x != 0) {
		//开辟一个大小为LNode的内存空间,并让j指向它
		j = (LNode*)malloc(sizeof(LNode));
		//为j输入数据
		j->data = x;
		//将j插入单链表中
		i->next = j;
		//i后移至j
		i = j;
		//循环输出数据
		cin >> x;
	}
	//将最后一个节点的next置空
	i->next = NULL;
	return L;
}

1.8.2.头插法

linkList listHeadInsert(linkList& L) {
	//为头结点分配内存空间
	L = (linkList)malloc(sizeof(linkList));
	L->next = NULL;
	LNode* i;
	Elemtype x;
	cin >> x;
	whil(x != 0) {
		//为节点i分配内存空间,并输入数据
		i = (LNode*)malloc(sizeof(LNode));
		i->data = x;
		//将i插入表中
		i->next = L->next;
		L->next = i;
		cin >> x;
	}
	return L;
}

2.双链表

2.1.双链表的初始化

typedef struct DNode {
	Elemtype data;
	struct DNode* prior, * next;
}DNode, *DLinkList;
//初始化双链表
bool initDLinkList(DLinkList& L) {
	//为头结点分配空间
	L = (DLinkList)malloc(sizeof(DLinkList));
	//内存分配失败
	if (L == NULL) return false;
	//头结点的前后指针置空
	L->prior = NULL;
	L->next = NULL;
	return true;
}
void testDLinkList() {
	//申明一个双链表
	DLinkList L;
	//初始化双链表
	initDLinkList(L);
	//后续操作
}

2.2.双链表的插入

bool insertNode(DNode* p, DNode* s) {
	//插入操作非法
	if (p == NULL && s == NULL) return false;
	s->next = p->next;
	//特殊情况:p为双链表中最后一个节点
	if (p->next != NULL) p->next->prior = s;
	s->prior = p;
	p->next = s;
	return true;
}

2.3.双链表的删除、销毁

//删除双链表当前节点的下个节点
bool deleteNextDNode(DNode* p) {
	//删除操作非法
	if (p == NULL) return false;
	//申明一个DNode*类型的指针q指向p的下一个节点
	DNode* q = p->next;
	//删除操作非法
	if (q == NULL) return false;
	//改变前后指针,并且释放p的下一个节点q的内存空间
	p->next = q->next;
    //q不是最后一个节点
	if (q->next != NULL) q->next->prior = p;
	free(q);
	return true;
}
//销毁双链表
void destroyDLinkList(DLinkList &L){
    //遍历双链表,并且释放下个节点
    while(L->next != NULL) deleteNextDNode(L);
    //释放头结点
    free(L);
    //头指针L置空
    L = NULL;
}

3.循环链表

typedef struct LNode {
	Elemtype data;
	struct LNode *next;
}LNode, *linkList;
//初始化循环链表
bool initList(linkList& L) {
	L = (linkList)malloc(sizeof(linkList));
	//内存分配失败
	if (L == NULL) return false;
	//将L的next指针指向自己
	L->next = L;
	return true;
}
//判断表空
bool isEmpty(linkList L) {
	if (L->next = L) return true;
	else return false;
}
//判断表尾
bool	isTail(LNode* p) {
	if (p->next == L) return true;
	else return false;
}

4.循环双链表

typedef struct DNode {
	elemtype data;
	struct DNode *prior, *next;
}DNode, DLinkList;
//初始化循环双链表
bool initDLinkList(DLinkList& L) {
	L = (DNode*)malloc(sizeof(DNode));
	if (L == NULL) return false;
	L->next = L;
	L->prior = L;
	return true;
}
//判断表空
bool isEmpty(DLinkList L) {
	if (L->next == L) return true;
	else return false;
}
//判断表尾
bool isTail(DLinkList L, DNode *p) {
	if (p->next == L) return true;
	else return false;
}
void testDLinkList() {
	DLinkList L;
	initDLinkList(L);
}

5.链表和顺序表的区别

5.1.逻辑结构

顺序表和链表都属于线性表

5.2.存储结构

  1. 顺序表
    1. 优点:存储密度大,支持随机存储
    2. 缺点:需要一片连续的存储空间,改变容量不方便
  2. 链表
    1. 优点:支持离散存储可以动态的修改容量
    2. 缺点:存储密度小(需要存放指针),不支持随机存储

5.3.基本操作

5.3.1.创建

  1. 顺序表:预分配一片连续的存储空间
    1. 静态分配:容量不可更改
    2. 动态分配:容量可以更改,但需要大量移动
  2. 链表:仅分配一个头结点(也可以仅声明一个头指针)

5.3.2.销毁

  1. 顺序表:修改length = 0,逻辑上销毁
    1. 静态分配:由系统自动回收
    2. 动态分配:需要手动free
  2. 链表:逐个free节点

5.3.3.增加/删除

  1. 顺序表:在增加/删除的位置后方逐一移动元素,时间复杂度为O(n)
  2. 链表:需找到该元素的所在位置,然后修改指针,时间复杂度为O(n)
  3. 虽然顺序表和链表的时间复杂度都为O(n),但是顺序表的操作为移动元素,链表的操作为查找元素,因此,顺序表所需时间远大于链表

5.3.4.查找

  1. 按位序查找
    1. 顺序表:由于有随机存储特性,因此,时间复杂度为O(1)
    2. 链表:需要按序遍历链表,因此,时间复杂度为O(n)
  2. 按值查找
    1. 顺序表:按序遍历顺序表,因此,时间复杂度为O(n),如果有序,可为O(lon2n)
    2. 链表:按序遍历链表,因此,时间复杂度为O(n)

6.王道课后题

void deleteX(linkList& L, int x) {
	//递归出口,遍历到表中最后一个元素或者表为空时执行
	if (L == NULL) return;
	//当前元素不为x,&L = L->next,执行下一层递归
	else if (L->data != x) { 
		deleteX(L->next, x); 
	}
	else {
		//当前元素为x,申明一个LNode*类型的指针p指向L
		LNode* p = L;
		//将L后移
		L = L->next;
		//释放指针p所指向的内存空间
		free(p);
		//进行下一层递归
		deleteX(L, x);
	}
}

void deleteX(linkList& L, elemType x) {
	//申明一个LNode*型的指针p,并使其指向头结点的下一个节点
	LNode* p = L->next;
	while (p!= NULL) {
		//当前节点的数据为x,则删除当前节点
		if (p->data == x) {
			if (p->next != NULL) {
				LNode* q = p->next;
				p->data = q->data;
				p->next = q->next;
				free(q);
			}
			else free(p);
		}
		//移动到下一个节点
		else p = p->next;
	}
	return;
}
void deleteX(linkList& L, elemType x) {
	//申明LNode*类型的指针p和指针q,并使其分别指向第一个节点和头结点
	//申明LNode*类型的指针s
	LNode* p = L->next, *q = L, *s;
	while (p != NULL) {
		if (p->data == x) {
			//s指向p所指向的节点
			s = p;
			//q的next指针指向p的next指针
			q->next = p->next;
			//p指针后移
			p = p->next;
			//释放s节点
			free(s);
		}
		else {
			//p、q同步后移
			p = p->next;
			q = q->next;
		}
	}
	return;
}

void reverse(linkList L) {
	//申明一个单链表A,并为其头结点分配空间
	linkList A = (LNode*)malloc(sizeof(LNode));
	//申明一个指针p指向单链表L的第一个节点
	LNode* p = L->next;
	//遍历单链表L,并且将他的每一个节点插入到单链表A的第一个节点(前插法)
	while (p != NULL) {
		LNode* q = (LNode*)malloc(sizeof(LNode));
		q->data = p->data;
		q->next = A->next;
		A->next = q;
		//插入完成后,指针p后移
		p = p->next;
	}
	//将指针q重新指向链表A的第一个节点
	q = A->next;
	//按序输出单链表A
	while (q != NULL) cout << q->data;
	return;
}
void reverseCout(linkList L) {
	//通过递归找到最后一个节点
	if (L->next != NULL) reverseCout(L->next);
	//输出除了头结点外的节点(逆序)
	if (L != NULL) cout << L->data;
}

void deleteMin(linkList& L) {
	if (L == NULL) return false;
	//申明p,q指针,p用来遍历单链表,q用来标记当前最小值节点
	LNode* p = L->next, * q = L->next;
	//遍历单链表
	while (p != NULL) {
		//找到更小值,更新最小值和q的指向节点
		if (p->data < q->data) q = p;
		//p指针后移
		p = p->next;
	}
	//删除最小值节点
	p = q->next;
	q->data = p->data;
	free(p);
	return true;
}
linkList deleteMin(linkList& L) {
	//p指向第一个节点,pPre指向p节点的前驱
	LNode* p = L->next, * pPre = L;
	//min指向当前最小值节点,minPre指向当前最小值节点前驱
	LNode* min = L->, * minPre = L;
	//遍历单链表
	while (p != NULL) {
		//当前节点的值更小则更新最小值相关节点
		if (p->data < min->data) {
			min = p;
			minPre = pPre;
		}
		//p相关节点后移
		pPre = p;
		p = p->next;
	}
	//删除最小值节点
	minPre->next = min->next;
	free(min);
	return L;
}

linkList reverse(linkList& L) {
	//p用来遍历单链表,s用来重新插入新节点
	LNode* p = L->next, *s;
	//将头结点的next指针置空
	//1.防止最后一个节点的next指针是野指针
	//2.因为p已经指向最开始单链表L的第一个节点,因此,不会丢失后继节点
	L->next = NULL;
	//遍历单链表
	while (p != NULL) {
		//新分配一块内存空间,并用s指向它
		s = (LNode*)malloc(sizeof(LNode));
		//将p赋值给s,并将s插入单链表的第一个节点
		s->data = p->data;
		s->next = L->next;
		L->next = s;
		//p后移一个节点
		p = p->next;
	}
	return L;
}
linkList reverse(linkList& L) {
	LNode* pre = L, * p = L->next, * q = p->next;
	//p节点是第一个节点,因此,逆置后是最后一个节点,需要将其next指针置空
	p->next = NULL;
	//遍历单链表
	while (q != NULL) {
		//三个指针依次后移一位
		pre = p;
		p = q;
		q = q->next;
		//将p的next指针逆置
		p->next = pre;
	}
	L->next = p;
	return L;
}

  1. 设表为L-3-1-5-4
  2. 分裂成表1:L-3,表2:1-5-4
  3. 表1已经为有序表,则将遍历表2,依次将表2的节点插入表1的适当位置
linkList sequence(linkList& L) {
	LNode* p = L->next, * pre = L;
	//特殊情况:表空
	if (p == NULL) return L;
	LNode* s = p->next, *q;
	//分割成两个表,将p的next指针置空
	p->next = NULL;
	while (s != NULL) {
		//遍历新链表找到第一个比p大的节点的前驱
		while (p != NULL && s->data > p->data) {
			pre = p;
			p = p->next;
		}
		//将q指向s的当前节点,s向后移
		q = s;
		s = s->next;
		//将q插入到新表中
		q->next = pre->next;
		pre->next = q;
		//重置p和pre指针
		p = L->next;
		pre = L;
	}
	return L;
}

  1. 遍历单链表,判断当前节点是否属于区间内
  2. 属于则删除,不属于则向后遍历
linkList deleteXtoY(linkList& L, int min, int max) {
	LNode* p = L->next, * pre = L;
	//遍历单链表
	while (p != NULL) {
		//在区间内
		if (p->data >= min && p->data <= max) {
			//删除元素并释放空间,修改前驱节点的next指针
			pre->next = p->next;
			//申明一个新指针s指向p,并向后移动p,释放s
			LNode* s = (LNode*)malloc(sizeof(LNode));
			s = p;
			p = p->next;
			free(s);
		}
		//不在空间内
		else {
			//pre和p向后移
			pre = p;
			p = p->next;
		}
	}
	return L;
}

  1. 分别获取两个链表的节点个数,并且得到其差值lengthGap
  2. 长链表向前移动lengthGap个节点,这样剩下的节点和短链表的个数一样
  3. 通过指针遍历剩下的长链表和短链表,并判断其值是否相等
//获取单链表节点个数
int getLen(linkList L) {
	LNode* p = L->next;
	int length = 0;
	while (p != NULL) {
		p = p->next;
		length++;
	}
	return length;
}
linkList sameLNode(linkList& a, linkList& b) {
	//
	int len1 = getLen(a), len2 = getLen(b);
	linkList longList, shortList;
	//lengthGap用于标记两个链表节点个数差
	int lengthGap;
	//个数多的设置为长表,个数少的设置为短表,记录差值
	if (len1 < len2) {
		shortList = a->next;
		longList = b->next;
		lengthGap = len2 - len1;
	}
	else {
		shortList = b->next;
		longList = a->next;
		lengthGap = len1 - len2;
	}
	//移动长表的指针,使得剩下的节点个数和短表一样
	while (lengthGap != 0) {
		longList = longList->next;
		lengthGap--;
	}
	//遍历剩余链表,对比两个链表当前元素
	while (longList != NULL) {
		if (longList->data == shortList->data) {
            //返回第一个公共节点的指针
			return longList;
		}
		longList = longList->next;
		shortList = shortList->next;
	}
	return NULL;
}

  1. 遍历单链表,每轮找到一个最小值节点,输出并释放该最小值节点
  2. 仅剩头结点时,释放头结点
​void coutAndFree(linkList& head) {
	LNode* p, * pre, * min, * minpre;
	//遍历单链表直到仅剩头结点
	while (head->next != NULL) {
		//每次循环重置各指针位置,其中min默认为除头结点外的第一个节点
		p = head->next;
		pre = head;
		min = p;
		minpre = pre;
		//特殊情况:表中只有一个元素,输出该元素
		if (p->next == NULL) cout << p->data;
		else {
			//遍历单链表
			while (p != NULL) {
				//找到当前最小值更小的节点,更新最小值相关指针
				if (p->data < min->data) {
					min = p;
					minpre = pre;
				}
				//p相关指针后移
				pre = p;
				p = p->next;
			}
		}
		//输出这一轮中的最小值
		cout << min->data;
		//删除最小值节点,并释放空间
		minpre->next = min->next;
		free(min);
	}
	//释放头结点
	free(head);
}

  1. 新建一个头结点B,并申明一个指针指向A的第一个节点
  2. 将A分裂成头结点A和其他节点
  3. 遍历A的其余节点,用一个变量标记奇偶,奇数则插入A,偶数插入B
linkList oddEven(linkList& A) {
	//为B声明一个头结点,并分配为其分配空间,置空next指针
	linkList B = (LNode*)malloc(sizeof(LNode));
	B->next = NULL;
	LNode* p = A->next, * q = B, * pre = A;
	//标记奇偶
	int count = 1;
	//遍历单链表
	while (p != NULL) {
		//偶数
		if (count % 2 == 0) {
			//申明一个节点s,并为其分配空间
			LNode* s = (LNode*)malloc(sizeof(LNode));
			//将p赋值给s
			s->data  = p->data;
			//将s插入单链表B中,并且q后移一位
			q->next = s;
			s->next = NULL;
			q = s;
			//p相关指针后移,并且释放p当前节点
			pre->next = p->next;
			free(p);
			p = pre->next;
		}
		//奇数p相关指针后移一位
		else {
			pre = p;
			p = p->next;
		}
		count++;
	}
	return B;
}
linkList oddEven(linkList& A) {
	//为B声明一个头结点,并分配为其分配空间,置空next指针
	linkList B = (LNode*)malloc(sizeof(LNode));
	B->next = NULL;
	//使得p指向链表A的第一个节点
	LNode* p = A->next;
	//设置指针指向链表的最后一个节点,采用尾插法(顺序插入)
	LNode* aTail = A, * bTail = B;
	//将A断链
	A->next = NULL;
	//count标记奇偶
	int count = 0;
	while (p != NULL) {
		count++;
		//偶数,将p插入表中,并且aTail向后移动
		if (count % 2 == 0) {
			aTail->next = p;
			aTail = aTail->next;
		}
		//奇数,将p插入表中,并且bTail向后移动
		else {
			bTail->next = p;
			bTail = bTail->next;
		}
		//p向后移动
		p = p->next;
	}
	//A和B的最后一个节点next指针置空
	aTail->next = NULL;
	bTail->next = NULL;
	return B;
}

 采用上题思路,一个表采用头插法一个表采用尾插法

linkList seperate(linkList& A) {
	//为B申明一个头结点,并将其next指针置空
	linkList B = (LNode*)malloc(sizeof(LNode));
	B->next = NULL;
	//用p指针指向A的节点,防止断链
	LNode* p = A->next, * aTail = A, * bTemp = B;
	//A的next指针置空,相当于清空其节点
	A->next = NULL;
	int count = 0;
	while (p != NULL) {
		count++;
		//偶数,采用头插法
		if (count % 2 == 0) {
			bTemp = p;
			p = p->next;
			bTemp->next = B->next;
			B->next = bTemp;
		}
		//奇数,采用尾插法
		else {
			aTail->next = p;
			aTail = aTail->next;
			p = p->next;
		}
	}
	//A表采用尾插法,需要置空最后一个节点的next指针
	aTail->next = NULL;
	return B;
}
  1. 将p用尾插法插入A中
  2. 向后移动,将p用头插法插入B中
linkList seperate(linkList& A) {
	//创建链表B
	linkList B = (LNode*)malloc(sizeof(LNode));
	B->next = NULL;
	LNode* p = A->next, * aTail = A, * q;
	while (p != NULL) {
		//p用尾插法插入A
		aTail->next = p;
		aTail = aTail->next;
		//p向后移动一个节点,并用q标记p的next节点
		p = p->next;
		q = p->next;
		//p用头插法插入B(逆序)
		p->next = B->next;
		B->next = p;
		//p移动到q的位置
		p = q;
	}
	//aTail的next指针置空
	aTail->next = NULL;
	return B;
}

  1. 设置三个指针,pre标记当前节点,p标记pre的后一个节点,通过比较两个节点的数值,达到目的
  2. 如果两个节点的值相同,则用temp指向p,p向后继续移动,释放temp,继续比较
  3. 如果两个节点的值不相同,则p和pre都向后移动
linkList deleleSameLNode(linkList& L) {
	LNode* p = L->next, * pre, * temp;
	//表中仅有一个元素
	if (p->next == NULL) return L;
	else {
		//循环遍历单链表
		while (p != NULL) {
			//p和pre向后移动一个节点
			pre = p;
			p = p->next;
			//找到第一个不与pre->data相等的节点,并且依次释放中间节点
			while (p != NULL && p->data == pre->data) {
				temp = p;
				p = p->next;
				free(temp);
			}
			//接上单链表
			pre->next = p;
		}
	}
	return L;
}

  1. 分别用指针p和q标记A和B头结点之后的第一个节点,然后将链表A清空,将B的头结点释放
  2. 判断p和q指向的节点的大小,小的则插入A,并向后移动一个节点,直到某个表空
  3. 输出剩下的那个表的剩余元素
linkList merge(linkList& A, linkList& B) {
	//用p和q分别遍历A和B链表
	LNode* p = A->next, * q = B->next, * temp;
	//将A的next指针置空,使其成为一个空链表
	A->next = NULL;
	//释放B的头结点
	free(B);
	//遍历A和B
	while (p != NULL && q != NULL) {
		//如果当前q节点小,则用头插法将其插入A
		if (q->data < p->data) {
			temp = q->next;
			q->next = A->next;
			A->next = q;
			q = temp;
		}
		//如果当前p节点小,则用头插法将其插入A
		else {
			temp = p->next;
			p->next = A->next;
			A->next = p;
			p = temp;
		}
	}
	//输出剩余表
	if (q == NULL) {
		while (p != NULL) {
			temp = p->next;
			p->next = A->next;
			A->next = p;
			p = temp;
		}
	}
	//输出剩余表
	if (p == NULL) {
		while (q != NULL) {
			temp = q->next;
			q->next = A->next;
			A->next = q;
			q = temp;
		}
	}
	return A;
}

  1. 设置两个指针p和q分别用来遍历链表A和B
  2. 判断p和q大小,相等则新建节点复制p的数据,并将其用尾插法插入C;不相等则后移较小的节点
​
linkList commonLNode(linkList& A, linkList& B) {
	//创建单链表C
	linkList C = (LNode*)malloc(sizeof(LNode));
	C->next = NULL;
	LNode* p = A->next, * q = B->next, * s = C;
	//用p和q指针分别遍历链表A和B
	while (p != NULL && q != NULL) {
		//pq指向的节点data相等,则新建一个节点temp,并用尾插法插入链表C
		//spq分别向后移动一个节点
		if (q->data == p->data) {
			LNode* temp = (LNode*)malloc(sizeof(LNode));
			temp->data = q->data;
			temp->next = NULL;
			s->next = temp;
			s = s->next;
			p = p->next;
			q = q->next;
		}
		//不相等,则更小的节点向后移动一个节点
		else if (q->data < p->data) q = q->next;
		else p = p->next;
	}
	//将C的最后一个节点的next指针置空
	s->next = NULL;
	return C;
}

​

思路同14 

linkList commonLNode(linkList& A, linkList& B) {
	LNode* p = A->next, * q = B->next, * s = A, * temp;
	//置空A表
	A->next = NULL;
	//遍历原A表和原B表
	while (p != NULL && q != NULL) {
		//相等,则将原A表的元素插入A中,并释放原B中的节点
		if (p->data == q->data) {
			s->next = p;
			s = s->next;
			p = p->next;
			temp = q;
			q = q->next;
			free(temp);
		}
		//不相等,释放较小的节点,并向后移动一个节点
		else if (p->data < q->data) {
			temp = p;
			p = p->next;
			free(temp);
		}
		else {
			temp = q;
			q = q->next;
			free(temp);
		}
	}
	//释放剩余表中的节点
	while (p) {
		temp = p;
		p = p->next;
		free(temp);
	}
	while (q) {
		temp = q;
		q = q->next;
		free(temp);
	}
	//将表A的最后一个节点置空
	s->next = NULL;
	//释放B的头结点
	free(B);
	return A;
}

  1. 子序列即B中每一个节点都按序存在于A某个节点之后
  2. 设置pa pb指针各自指向A B链表
  3. 外层循环遍历链表A,每移动到一个新的A节点,则开始内层循环,匹配接下来的元素是否都相等,不相等则pa向后移动,pb重置为第一个节点
bool subsequence(linkList& A, linkList& B) {
	LNode* pa = A->next, * pb = B, *temp;
	//遍历链表A
	while (pa) {
		//用temp保存pa的下一个节点,用于下次遍历
		temp = pa->next;
		//重置pb为B头结点后的第一个节点
		pb = B->next;
		//遍历链表B
		while (pb) {
			//pa与pb不等则跳出循环
			if (pb->data != pa->data) break;
			//否则pa和pb都向后移动一个节点
			pa = pa->next;
			pb = pb->next;
		}
		//判断B是否为A的子序列的条件是是否在遍历完B之前跳出循环,即pb是否到B的最后一个节点指向的NULL
		if (pb == NULL) return true;
		//将pa重置为temp指向的节点,开启下一次循环
		pa = temp;
	}
	return false;
}

用pq指针分别标记链表的头尾,遍历单链表,判断是否pq的data是否相等

int getLen(DLinkList L) {
	int length = 0;
	DNode* p = L;
	while (p->next != L) {
		p = p->next;
		length++;
	}
	return length;
}
bool isSymmetry(DLinkList L) {
	//获得该循环双链表的长度
	int length = getLen(L);
	//每次循环pq指针各移动一个节点,因此总长度要除以2
	//这样也能解决奇偶数的问题
	//奇数的最中间节点不管如何都不影响对称性
	length /= 2;
	//p指向第一个节点,q指向最后一个节点
	DNode* p = L->next, * q = L->prior;
	//通过length为标记判断是否走完整个链表
	while (length) {
		//pq不相等则退出循环,此时length不可能为0
		if (p->data  != q->data) break;
		//每次移动length-1,p向后移动,q向前移动
		else {
			length--;
			p = p->next;
			q = q->prior;
		}
	}
	//length不为0,则不对称;为0,则对称
	if (length) return false;
	else return true;
}

linkList merge(linkList& A, linkList& B) {
	LNode* pa = A->next, * pb = B->next;
	//pa和pb分别遍历到A和B的最后一个节点
	while (pa->next != A) pa = pa->next;
	while (pb->next != B) pb = pb->next;
	//将B的第一个节点接到A的最后一个节点后
	pa->next = B->next;
	//修改B的最后一个节点的next指针为A的头结点
	pb->next = A;
	//释放B的头结点
	free(B);
	return A;
}

bool deleteAndDestroy(linkList& L) {
	LNode* p, * pre, *min, *minpre;
	//遍历单链表
	while (L->next != L) {
		pre = L;
		p = L->next;
		min = p;
		minpre = pre;
		//找到当前链表中最小的节点
		while (p != L) {
			if (p->data < min->data) {
				min = p;
				minpre = pre;
			}
			pre = p;
			p = p->next;
		}
        cout << min->data << endl;
		//在链表中删除该元素,并释放该节点
		minpre->next = min->next;
		free(min);
	}
	//释放头结点
	free(L);
	return true;
}

DLinkList Locate(DLinkList& L, int x) {
	DNode* p = L->next, *ppre = L, *q, *qpre;
	//遍历单链表,找到链表中x在当前链表中的位置
	while (p->data != x) {
		ppre = p;
		p = p->next;
	}
	//频度+1
	p->freq++;
	//p不为最后一个节点
	if (p->next) {
		ppre->next = p->next;
		p->next->prior = ppre;
	}
	//p为最后一个节点
	else ppre->next = NULL;
	//找到第一个频度小于等于p的节点
	q = L->next, qpre = L;
	while (p->freq < q->freq) {
		qpre = q;
		q = q->next;
	}
	//q不为最后一个节点
	if (q->next) {
		qpre->next = p;
		q->prior = p;
		p->next = q;
		p->prior = qpre;
	}
	//q为最后一个节点
	else {
		qpre->next = p;
		p->next = NULL;
	}
	return p;
}

bool isCircle(linkList L) {
	//设置快慢指针
	LNode* fast = L->next, * slow = L->next;
	//快指针一次移动两个节点,慢指针一次移动一个节点
	//循环结束条件为快慢指针相遇或者快指针碰到NULL
	while (fast->next != NULL && fast != NULL) {
		fast = fast->next->next;
		slow = slow->next;
		if (fast == slow) break;
	}
	//快指针结束时不为NULL则有环,返回true;否则无环,返回false
	if (fast) return true;
	else return false;
}

 

  1. 设置pq指针都指向头结点后的一个节点,p向后移动k个节点,若未能移动k个节点p就指向了NULL,则返回false,说明k的大于表的长度
  2. p在q移动到k个节点后,pq一起移动,直到p移动到NULL,此时q的位置就为倒数第k个节点,输出q
bool searchLNode(linkList L, int k) {
	LNode* p = L->next, * q = L->next;
	//使得p移动到第k个节点
	while (k > 0 && p != NULL) {
		p = p->next;
		k--;
	}
	//k值大于表长
	if (k > 0) return false;
	//找到倒数第k个元素的位置
	while (p) {
		p = p->next;
		q = q->next;
	}
	cout << q->data << endl;
	return true;
}

int getLen(linkList& L) {
	int length = 1;
	LNode* p = L->next;
	while (p->next) {
		p = p->next;
		length++;
	}
	return length;
}
linkList commonLNode(linkList& A, linkList& B) {
	linkList longList, shortList;
    //得到AB链表的长度
	int lenA = getLen(A);
	int lenB = getLen(B);
	int lengthGap;
    //设置长链表和短链表,并求其长度差值
	if (lenA < lenB) {
		longList = B;
		shortList = A;
		lengthGap = lenB - lenA;
	}
	else {
		longList = A;
		shortList = B;
		lengthGap = lenA - lenB;
	}
	LNode* p = longList->next, * q = shortList->next,* ptemp, *qtemp;
    //长表移动两表差值个节点
	while (lengthGap) {
		p = p->next;
		lengthGap--;
	}
    //求公共节点
	while (p) {
		if (p->data == q->data) {
			ptemp = p;
			qtemp = q;
			while (ptemp != NULL && ptemp->data == qtemp->data) {
				ptemp = ptemp->next;
				qtemp = qtemp->next;
			}
			if (ptemp == NULL) return p;
		}
		p = p->next;
		q = q->next;
	}
	return NULL;
}

         

//获得绝对值
int inverseNum(int a) {
	if (a >= 0) return a;
	else return -a;
}
linkList deleteSameLNode(linkList& L, int n) {
    //申请n+1个元素的数组,用于标记出现的数字的绝对值
	int* arr = (int*)malloc(sizeof(int) * (n + 1));
    //初始化数组元素为0
	for (int i = 0; i < n + 1; i++) arr[i]= 0;
	LNode* p = L->next, *pre = L, *temp;
    //遍历单链表
	while (p) {
        //将其结果转换为绝对值
		int i = inverseNum(p->data);
        //该数出现过,则删除该节点
		if (arr[i]) {
			temp = p->next;
			pre->next = p->next;
			free(p);
			p = temp;
		}
        //该数未出现过,pre和p向后移,并且在数组中标记
		else {
			arr[i]++;
			pre = p;
			p = p->next;
		}
	}
	return L;
}

int getLen(linkList& L) {
	int length = 1;
	LNode* p = L->next;
	while (p->next) {
		p = p->next;
		length++;
	}
	return length;
}
linkList rearrange(linkList& L) {
	//获取表长
	int length = getLen(L);
	LNode* p = L, *q, *temp, *s;
	int dist = 0;
	//移动p,使p的下一个节点为下半链表的第一个节点
	if (length % 2) dist = length / 2 + 1;
	else dist = length / 2;
	while (dist) {
		p = p->next;
		dist--;
	}
	//temp标记后半链表的第一个节点
	q = p->next;
	temp = q;
	//将表分裂为前半和后半两个部分
	p->next = NULL;
	//将q指针移动到后半链表的最后一个节点
	while (q->next) q = q->next;
	//将后半链表进行逆序
	while (temp != q) {
		s = temp->next;
		temp->next = q->next;
		q->next = temp;
		temp = s;
	}
	//将p重置为L的头结点后的第一个节点
	//逆序后,q指向的就是后半链表逆序后的第一个节点
	p = L->next;
	//将表置空
	L->next = NULL;
	s = L;
	//依次从两个表取节点采用尾插法插入L中
	int count = 1;
	while (count <= length) {
		if (count % 2) {
			temp = p->next;
			s->next = p;
			p = temp;
		}
		else {
			temp = q->next;
			s->next = q;
			q = temp;
		}
		s = s->next;
		count++;
	}
	//将表中最后一个节点的next指针置空
	s->next = NULL;
	return L;
}
举报

相关推荐

0 条评论