线性表和一些题

1.线性表是最常用且最简单的一种数据结构。

1>存在唯一的一个北城做“第一个”的数据元素;

2>存在唯一的一个被称做“最后一个”的数据元素;

3>除第一个之外,集合中的每个数据元素均只有一个前驱;

4>除最后一个之外,集合中每个数据元素均只有一个后继。

图1

2.定长顺序表

sqlist.h

#pragma once

//定长的顺序表结构体定义
typedef struct Sqlist
{
	int arr[10];//数据域
	int length;//有效数据节点的个数
}Sqlist, *PSqlist;

//typedef struct Sqlist Sqlist;
//typedef struct Sqlist* PSqlist;



//初始化
void Init_sqlist(PSqlist plist);//struct Sqlist* plist

//按位置插入  
bool Insert_Pos(PSqlist plist, int pos, int val);

//按值删  删除这个值出现的第一次的位置
bool Del_val(PSqlist plist, int val);

//按位置删除
bool Del_pos(PSqlist plist, int pos);

//查找值为val的节点
int Search(PSqlist plist, int val);

//判空
bool IsEmpty(PSqlist plist);

//判满
bool IsFull(PSqlist plist);

//清空
void Clear(PSqlist plist);

//销毁
void Destroy(PSqlist plist);

sqlist.cpp

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "sqlist.h"

//初始化
void Init_sqlist(PSqlist plist)//struct Sqlist* plist
{
	assert(plist != NULL);
	if(plist == NULL)
		return;

	//plist->arr;//不需要初始化
	plist->length = 0;
}

//按位置插入  
bool Insert_Pos(PSqlist plist, int pos, int val)
{
	//assert
	if(pos<0 || pos>plist->length)
		return false;
	if(IsFull(plist))
	{
		return false;
	}

	for(int i=plist->length-1; i>=pos; i--)
	{
		plist->arr[i+1] = plist->arr[i];
	}
	//此时 for循环结束 标志着插入位置已经清空  将位置留出来了
	
	plist->arr[pos] = val;
	plist->length++;
	return true;
}

//按值删  删除这个值出现的第一次的位置
bool Del_val(PSqlist plist, int val)
{
	assert(plist != NULL);
	if(plist == NULL)
		return false;

	//如果为NULL  则不许删除
	if(IsEmpty(plist))
	{
		return false;
	}
	int pos = Search(plist, val);//i是Search的返回值
	if(pos == -1)//pos==-1  代表没找到  则返回假
	{
		return false;
	}

	//for(int i=pos; i+1<plist->length; i++)
	//{
	//	plist->arr[i] = plist->arr[i+1];
	//}
	此时for循环执行完毕 代表待删除节点后的值统一向前覆盖了

	//plist->length--;//删除了一个节点  则有效长度length-- 一下

	Del_pos(plist, pos);
	return true;

}

//按位置删除
bool Del_pos(PSqlist plist, int pos)
{
	assert(plist!=NULL && pos>=0 && pos<plist->length);
	if(plist==NULL || pos<0 || pos>=plist->length)
	{
		return false;
	}

	
	//if(IsEmpty(plist));  这一行代码 执行不到   无意义


	for(int i=pos; i+1<plist->length; i++)
	{
		plist->arr[i] = plist->arr[i+1];
	}
	//此时for循环执行完毕 代表待删除节点后的值统一向前覆盖了

	plist->length--;//删除了一个节点  则有效长度length-- 一下
	return true;

}

//查找值为val的节点
int Search(PSqlist plist, int val)
{
	assert(plist!=nullptr);

	for(int i=0; i<plist->length; i++)
	{
		if(plist->arr[i] == val)
		{
			return i;
		}
	}
	return -1;
}

//判空
bool IsEmpty(PSqlist plist)
{
	return plist->length==0;
}

//判满
bool IsFull(PSqlist plist)
{
	return plist->length==10;
}

//清空
void Clear(PSqlist plist)
{
	plist->length = 0;
}

//销毁
void Destroy(PSqlist plist);

3.不定长的顺序表

Dsqlist.h

#pragma once 

typedef int ELEM_TYPE;
#define INIT_SIZE 10//默认开始的时候的空间个数


typedef struct DSQlist
{
	ELEM_TYPE *data;//用来承接我malloc申请的动态内存
	int length;//有效数据长度
	int listsize;//保存当前最大容量个数(以sizeof(ELEM_TYPE)为单位)
}DSQlist, *PDSQlist;


//下面写可执行函数声明:
//不外乎增删改查

//初始化
void Init_sqlist(PDSQlist plist);//struct Sqlist* plist

//按位置插入  
bool Insert_Pos(PDSQlist plist, int pos, int val);

//按值删  删除这个值出现的第一次的位置
bool Del_val(PDSQlist plist, int val);

//按位置删除
bool Del_pos(PDSQlist plist, int pos);

//查找值为val的节点
int Search(PDSQlist plist, int val);

//判空
bool IsEmpty(PDSQlist plist);

//判满
bool IsFull(PDSQlist plist);

//清空
void Clear(PDSQlist plist);

//销毁
void Destroy(PDSQlist plist);

//扩容
void Inc(PDSQlist plist);//data   


void Show(PDSQlist plist);

Dsqlist.cpp

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "Dsqlist.h"


//初始化
void Init_sqlist(PDSQlist plist)//struct Sqlist* plist
{
	//assert
	plist->data = (ELEM_TYPE*)malloc(sizeof(ELEM_TYPE) * INIT_SIZE);
	assert(plist->data != NULL);

	plist->length = 0;
	plist->listsize = INIT_SIZE;

}
//按位置插入  
bool Insert_Pos(PDSQlist plist, int pos, int val)
{
	//assert  plist  pos
	//判满 扩容
	if(IsFull(plist))
	{
		Inc(plist);
	}

	//将pos以及其之后的值统一向后挪动一位  将位置空出来
	for(int i=plist->length-1; i>=pos; i--)
	{
		plist->data[i+1] = plist->data[i];
	}
	//插入
	plist->data[pos] = val;
	plist->length++;
	return true;
}

//按值删  删除这个值出现的第一次的位置
bool Del_val(PDSQlist plist, int val)
{
	//assert
	if(IsEmpty(plist))
		return false;

	int pos = Search(plist, val);
	if(pos == -1)
		return false;

	return Del_pos(plist, pos);
}

//按位置删除
bool Del_pos(PDSQlist plist, int pos)
{
	//assert plist pos 
	//判满操作没有必要  因为对pos的合法性已经做了判断
	for(int i=pos; i+1<plist->length; i++)
	{
		plist->data[i] = plist->data[i+1];
	}

	plist->length--;
	return true;
}

//查找值为val的节点
int Search(PDSQlist plist, int val)
{
	//assert
	for(int i=0; i<plist->length; i++)
	{
		if(plist->data[i] == val)
		{
			return i;
		}
	}
	return -1;
}

//判空
bool IsEmpty(PDSQlist plist)
{
	return plist->length == 0;
}

//判满
bool IsFull(PDSQlist plist)
{
	return plist->length == plist->listsize;
}

//清空
void Clear(PDSQlist plist)
{
	plist->length = 0;
}

//销毁
void Destroy(PDSQlist plist)
{
	free(plist->data);
	plist->data = NULL;

	plist->length = plist->listsize = 0;
}

//扩容
void Inc(PDSQlist plist)//data   
{
	//malloc  realloc  calloc
	//库容 realloc  
	plist->data = (ELEM_TYPE*)realloc(plist->data, sizeof(ELEM_TYPE) * plist->listsize * 2);
	if(plist->data == NULL)
	{
		return;
	}

	//plist->length; 有效值不需要改变
	plist->listsize *= 2;
}

void Show(PDSQlist plist)
{
	//assert
	for(int i=0; i<plist->length; i++)
	{
		printf("%d ", plist->data[i]);
	}
	printf("\n");
}

顺序表的特点:
1.简单 好实现
2.逻辑上相邻,物理上也相邻
3.插入时间复杂度O(n)(挪动数据) 尾插的时间复杂度为O(1)
4.删除时间复杂度O(n)(挪动数据) 尾删的时间复杂度为O(1)
5.随机访问时间复杂度O(1) 因为有数组下标帮忙

4.单链表(带头结点)

特点:逻辑上相邻,但物理上不一定相邻

图2

两种for循环

1>for(Node* p=plist;p!=NULL;p=p->next)

2>for(Node* p=plist->next;p!=NULL;p=p->next)

list.h

#pragma once

//带头结点的单链表的结构体设计:
typedef struct Node
{
	int data;//数据域  保存节点有效值
	struct Node *next;//指针域 指向下一个节点的地址
}Node, *PNode;

/*  如果头结点想单独设计  则为下面代码  
struct Head
{
    struct Node* next;
};
*/

//所以头结点有两种处理方案:
//1.给头结点单独设计一套结构体
//2.使用普通数据节点的结构体设计   其中的数据域不使用  只使用其指针域


//可以实现的操作 声明
//增删改查

//初始化函数(给头结点赋值)
void Init_list(PNode plist);

//头插
bool Insert_Head(PNode plist, int val);//O(1)

//尾插
bool Insert_Tail(PNode plist, int val);//O(n)

//按位置插  pos
bool Insert_pos(PNode plist, int pos, int val);//O(n)

//头删
bool Del_Head(PNode plist);//O(1)

//尾删
bool Del_Tail(PNode plist);//O(n)

//按位置删 pos
bool Del_Pos(PNode plist, int pos);//O(n)

//按值删(将值为val的第一个节点删除掉)
bool Del_val(PNode plist, int val);//O(n)

//查找 val 在plist这个单链表中找值为val的第一个节点   找到后返回其地址  不然返回NULL
struct Node * Search(PNode plist, int val);//O(n)

//判空
bool IsEmpty(PNode plist);

//判满 单链表不需要判满

//获取前驱 (将值为val的节点去前一个节点返回)
struct Node* Get_Prior(PNode plist, int val);

//获取后继 (将值为val的节点去后一个节点返回)
struct Node* Get_Next(PNode plist, int val);

//获取其有效节点个数
int Get_Length(PNode plist);

//清空
void Clear(PNode plist);

//销毁1
void Destroy1(PNode plist);
//销毁2
void Destroy2(PNode plist);

void Show(PNode plist);

list.cpp

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "list.h"


//初始化函数(给头结点赋值)
void Init_list(PNode plist)
{
	assert(plist != NULL);
	if(plist == NULL)
	{
		return;
	}

	//plist->data;  头结点的数据域不使用
	plist->next = NULL;
}

//头插
bool Insert_Head(PNode plist, int val)
{
	assert(plist!=nullptr);

	//1.创建一个新节点
	struct Node * pnewnode = (Node*)malloc(sizeof(Node) * 1);
	assert(pnewnode != NULL);
	pnewnode->data = val;
	pnewnode->next = NULL;//此行代码 可加可不加 因为紧接着就会对pnewnode->next 重新赋值

	//2.找到合适的插入位置(头插函数  这一步不需要处理)

	//3.插入新节点  1和2 顺序不能颠倒   
	pnewnode->next = plist->next;//1
	plist->next = pnewnode;//2

	return true;
}

//尾插
bool Insert_Tail(PNode plist, int val)
{
	assert(plist!=nullptr);

	//1.创建新节点
	PNode pnewnode = (PNode)malloc(sizeof(Node) * 1);
	assert(pnewnode != NULL);
	pnewnode->data = val;
	pnewnode->next = NULL;  //这一步需不需要?  如果56行代码有的话  则不需要  没有的话这一行代码就必须写

    //2.先找到最后一个节点
	Node *p=plist;
	for(p; p->next!=NULL; p=p->next);  //此时for循环结束 p指向的是尾结点

    //3.给它后面插入
	pnewnode->next = p->next;
	p->next = pnewnode;
	return true;
}

//按位置插  pos
bool Insert_pos(PNode plist, int pos, int val)
{
	assert(plist!=NULL && pos>=0 && pos<=Get_Length(plist));
	if(plist == NULL || pos<0 || pos>Get_Length(plist))
		return false;

	//1.创建新节点
	PNode pnewnode = (PNode)malloc(sizeof(Node) * 1);
	assert(pnewnode != NULL);
	pnewnode->data = val;

	//2.找到合适的插入位置
	Node*p = plist;
	for(int i=0; i<pos; i++)
	{
		p=p->next;
	}
	//此时 p就指向待插入位置的前驱

	//3.给它后面插入
	pnewnode->next = p->next;
	p->next = pnewnode;

	return true;
}

//头删
bool Del_Head(PNode plist)
{
	assert(plist!=nullptr);
	if(IsEmpty(plist))
		return false;

	//1.申请一个临时变量指向要删除的第一个有效节点
	Node *p = plist->next;

	//2.将要删除的节点左右连接起来
	plist->next  = p->next;//plist->next = plist->next->next;

	//3.将要删除的节点内存释放,防止内存泄露
	free(p);
	p = NULL;

	return true;
}

//尾删
bool Del_Tail(PNode plist)
{
	assert(plist!=nullptr);

	//1.先找到要删除节点的上一个节点(前驱)
	Node *p =plist;
	for(p; p->next!=NULL; p=p->next)
	{
		if(p->next->next == NULL)
		{
			break;
		}
	}
	//此时p就指向倒数第二个节点

	//2.申请一个临时变量指向要删除的有效节点
	Node *q = p->next;

	//3.跨越指向(将要删除的节点左右连接起来)
	p->next = q->next;//p->next = p->next->next;

	//4.释放内存
	free(q);

	return true;
}

//按位置删 pos
bool Del_Pos(PNode plist, int pos)
{
	assert(plist!=NULL && pos>=0 && pos<Get_Length(plist));
	if(plist==NULL || pos<0 || pos>=Get_Length(plist))
		return false;

	
	//1.找到删除节点的上一个节点
	Node *p = plist;
	for(int i=0; i<pos; i++)
	{
		p = p->next;
	}
	//此时p就指向pos指向节点的前一个节点

	//2.申请一个临时变量指向要删除的有效节点
	Node *q = p->next;

	//3.跨越指向(将要删除的节点左右连接起来)
	p->next = q->next;

	//4.释放内存
	free(q);

	return true;
}

//按值删(将值为val的第一个节点删除掉)
bool Del_val(PNode plist, int val)
{
	assert(plist!=nullptr);
	Node *p = Get_Prior(plist, val);
	if(p==NULL)
	{
		return false;
	}

	Node *q = p->next;
	p->next = q->next;
	free(q);
	q = NULL;

	return true;

}

//查找 val 在plist这个单链表中找值为val的第一个节点   找到后返回其地址  不然返回NULL
struct Node * Search(PNode plist, int val)
{
	//不需要前驱的操作  所以 用第二种for循环
	assert(plist!=nullptr);
	Node *p = plist->next;
	for(p; p!=NULL; p=p->next)
	{
		if(p->data == val)
		{
			return p;
		}
	}
	return NULL;
}

//判空
bool IsEmpty(PNode plist)
{
	return plist->next == NULL;
}

//判满 单链表不需要判满

//获取前驱 (将值为val的节点去前一个节点返回)
struct Node* Get_Prior(PNode plist, int val)
{
	assert(plist!=nullptr);
	Node *p=plist;
	for(p; p->next!=NULL; p=p->next)
	{
		if(p->next->data == val)
		{
			return p;
		}
	}

	return NULL;
}

//获取后继 (将值为val的节点去后一个节点返回)
struct Node* Get_Next(PNode plist, int val)
{
	Node *p = Search(plist, val);
	if(p==NULL)
	{
		return NULL;
	}

	return p->next;
}

//获取其有效节点个数
int Get_Length(PNode plist)
{
	assert(plist!=nullptr);
	int count = 0;
	Node *p = plist->next;
	for(p; p!=NULL; p=p->next)
	{
		count++;
	}

	return count;
}

//清空
void Clear(PNode plist)
{
	Destroy1(plist);
}

//销毁1   一致头删   这个一定掌握(只用了一个指针)
void Destroy1(PNode plist)
{
	assert(plist!=nullptr);
	/*while(!IsEmpty(plist))
	{
		Del_Head(plist);
	}*/

	while(plist->next != NULL)
	{
		Node *p = plist->next;
		plist->next = p->next;
		free(p);
		p = NULL;
	}
}

//销毁2  不借助头节点(两个指针)  能理解就理解 理解不了记住第一种头删的方式即可
void Destroy2(PNode plist)
{
	assert(plist!=nullptr);
	Node *p = plist->next;
	Node *q;

	while(p != NULL)
	{
		q = p->next;
		free(p);
		p = q;
	}

	plist->next = NULL;
}

void Show(PNode plist)
{
	assert(plist!=nullptr);
	Node *p = plist->next;
	for(p; p!=NULL; p=p->next)
	{
		printf("%d ", p->data);
	}

	printf("\n");
}

5.单链表(不带头结点)

图3

两种for循环

1>for(Node* p=phead;p!=NULL;p=p->next)

2>for(Node* p=phead->next;p!=NULL;p=p->next)

NoHeadList.h

#pragma once 

//不带头结点的单链表的结构体设计:
typedef struct Node
{
	int data;//数据域  保存节点有效值
	struct Node *next;//指针域 指向下一个节点的地址
}Node, *PNode;


//增删改查

//初始化函数(给头结点赋值)
void No_Head_Init_list(PNode *phead);

//头插
bool No_Head_Insert_Head(PNode *phead, int val);

//尾插
bool No_Head_Insert_Tail(PNode *phead, int val);

//按位置插  pos
bool No_Head_Insert_pos(PNode *phead, int pos, int val);

//头删
bool No_Head_Del_Head(PNode *phead);

//尾删
bool No_Head_Del_Tail(PNode *phead);

//按位置删 pos
bool No_Head_Del_Pos(PNode *phead, int pos);

//按值删(将值为val的第一个节点删除掉)
bool No_Head_Del_val(PNode *phead, int val);

//查找 val 在phead这个单链表中找值为val的第一个节点   找到后返回其地址  不然返回NULL
struct Node * No_Head_Search(PNode *phead, int val);

//判空
bool No_Head_IsEmpty(PNode *phead);

//判满 单链表不需要判满

获取前驱 (将值为val的节点去前一个节点返回)
//struct No_Head_Node* Get_Prior(PNode *phead, int val);
//
获取后继 (将值为val的节点去后一个节点返回)
//struct Node* No_Head_Get_Next(PNode *phead, int val);

//获取其有效节点个数
int No_Head_Get_Length(PNode *phead);

//清空
void No_Head_Clear(PNode *phead);

//销毁1
void No_Head_Destroy1(PNode *phead);
//销毁2
void No_Head_Destroy2(PNode *phead);

void No_Head_Show(PNode *phead);

NoHeadList.cpp

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "NoHeadList.h"


//增删改查

//初始化函数(给头指针赋初始值)
void No_Head_Init_list(PNode *phead)//Pnode *  ==  Node **
{
	*phead = NULL;//*phead 保存的是第一个有效数据的地址  不确定的情况下NULL
}

//头插
bool No_Head_Insert_Head(PNode *phead, int val)
{
	assert(phead!=nullptr);
	//1.创建新节点
	Node *pnewnode = (Node*)malloc(sizeof(Node) * 1);
	assert(pnewnode != NULL);
	pnewnode->data = val;

	//2.找到合适的插入位置  头插
	//3.插入
	//带头结点的代码
	//pnewnode->next = plist->next;
	//plist->next = pnenwode;

	pnewnode->next = *phead;
	*phead = pnewnode;
	return true;
}

//尾插
bool No_Head_Insert_Tail(PNode *phead, int val)
{
	assert(phead!=nullptr);
	/*Node *p = plist;
	for(p; p->next!=NULL; p=p->next);*/

	if(*phead == NULL)
	{
		return No_Head_Insert_Head(phead, val);
	}
	//此时保证至少有一个数据节点

	Node *p = *phead; //NULL
	for(p; p->next!=NULL; p=p->next);

	Node *pnewnode = (Node*)malloc(sizeof(Node) * 1);
	assert(pnewnode != NULL);
	pnewnode->data = val;

	pnewnode->next = p->next;
	p->next = pnewnode;
	return true;
}

//按位置插  pos
bool No_Head_Insert_pos(PNode *phead, int pos, int val)
{
	assert(phead!=NULL && pos>=0 && pos<=No_Head_Get_Length(phead));
	if(pos == 0)//pos == 0  调用头插
	{
		return No_Head_Insert_Head(phead, val);
	}
	//下面的这个if 不会影响最终结果  只是为了和上面保持一致
	if(pos == No_Head_Get_Length(phead))//pos == length  调用尾插
	{
		return No_Head_Insert_Tail(phead, val);
	}

	Node *pnewnode = (Node*)malloc(sizeof(Node) * 1);
	assert(pnewnode != NULL);
	pnewnode->data = val;
	Node *p = *phead;
	for(int i=1; i<pos; i++)
	{
		p=p->next;
	}

	pnewnode->next = p->next;
	p->next = pnewnode;
	return true;
}

//头删
bool No_Head_Del_Head(PNode *phead)
{
	assert(phead!=nullptr);
	if(No_Head_IsEmpty(phead))
	{
		return false;
	}

	Node *q = *phead;
	*phead = q->next;
	free(q);
	return true;
}

//尾删
bool No_Head_Del_Tail(PNode *phead)
{
	assert(phead!=nullptr);
	if(No_Head_IsEmpty(phead))
	{
		return false;
	}

	if((*phead)->next == NULL)//这个if如果为真  代表只有一个有效数据节点
	{
		return No_Head_Del_Head(phead);
	}

	Node *p = *phead;
	for(p; p->next->next!=NULL; p=p->next);
	Node *q = p->next;
	p->next = q->next;
	free(q);
	return true;
}

//按位置删 pos
bool No_Head_Del_Pos(PNode *phead, int pos)
{
	assert(phead!=NULL && pos>=0 && pos<No_Head_Get_Length(phead));

	if(pos == 0)
	{
		return No_Head_Del_Head(phead);
	}

	Node *p = *phead;
	for(int i=1; i<pos; i++)
	{
		p=p->next;
	}

	Node *q = p->next;
	p->next = q->next;//p->next = p->next->next;
	free(q);
	return true;
}

//按值删(将值为val的第一个节点删除掉)
bool No_Head_Del_val(PNode *phead, int val)
{
	assert(phead!=nullptr);
	if(No_Head_IsEmpty(phead))
	{
		return false;
	}
	if((*phead)->data == val)
	{
		return No_Head_Del_Head(phead);
	}


	Node *p = *phead;
	for(p; p->next!=NULL; p=p->next)
	{
		if(p->next->data == val)
		{
			Node *q = p->next;
			p->next = q->next;
			free(q);
			return true;
		}
	}
	return false;
}

//查找 val 在phead这个单链表中找值为val的第一个节点   找到后返回其地址  不然返回NULL
struct Node * No_Head_Search(PNode *phead, int val)
{
	Node *p = *phead;
	for(p; p!=NULL; p=p->next)
	{
		if(p->data == val)
		{
			return p;
		}
	}
	return NULL;
}

//判空
bool No_Head_IsEmpty(PNode *phead)
{
	return *phead == NULL;
}

//判满 单链表不需要判满

获取前驱 (将值为val的节点去前一个节点返回)
//struct No_Head_Node* Get_Prior(PNode *phead, int val);
//
获取后继 (将值为val的节点去后一个节点返回)
//struct Node* No_Head_Get_Next(PNode *phead, int val);

//获取其有效节点个数
int No_Head_Get_Length(PNode *phead)
{
	int count = 0;
	Node *p = *phead;
	for(p; p!=NULL; p=p->next)
	{
		count++;
	}
	return count;
}

//清空
void No_Head_Clear(PNode *phead)
{
	No_Head_Destroy1(phead);
}

//销毁1
void No_Head_Destroy1(PNode *phead)
{
	/*while(!No_Head_IsEmpty(phead))
	{
		No_Head_Del_Head(phead);
	}*/

	while(*phead != NULL)
	{
		Node *q = *phead;
		*phead = q->next;
		free(q);
	}

}
//销毁2
void No_Head_Destroy2(PNode *phead)
{
	Node *p = *phead;
	Node *q;

	while(p!=NULL)
	{
		q = p->next;
		free(p);
		p = q;
	}

	*phead = NULL;
}

void No_Head_Show(PNode *phead)
{
	Node *p = *phead;
	for(p; p!=NULL; p=p->next)
	{
		printf("%d ", p->data);
	}
	printf("\n");
}

6.双向链表

图4

两种for循环

1>for(DNode* p=plist;p!=NULL;p=p->next)

2>for(DNode* p=plist->next;p!=NULL;p=p->next)

Dlist.h

#pragma once 

//双向链表结构体设计
typedef struct DNode
{
	int data;//数值域
	struct DNode* next;//保存下一个节点的地址
	struct DNode* prior;//保存上一个节点的地址
}DNode, *PDNode;


//初始化
void Init_Dlist(struct DNode *plist);

//头插
bool InsertHead(PDNode plist, int val);

//尾插
bool InsertTail(PDNode plist, int val);

//按位置插
bool InsertPos(PDNode plist, int pos, int val);

//头删
bool DelHead(PDNode plist);

//尾删
bool DelTail(PDNode plist);

//按位置删
bool DelPos(PDNode plist, int pos);

//按值删
bool DelVal(PDNode plist, int val);

//查找
struct DNode *Search(PDNode plist, int val);

//判空
bool IsEmpty(PDNode plist);

//获取有效元素个数
int Get_length(PDNode plist);

//打印
void Show(PDNode plist);

//清空
void Clear(PDNode plist);

//销毁
void Destroy1(PDNode plist);
void Destroy2(PDNode plist);

Dlist.cpp

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "Dlist.h"


//初始化
void Init_Dlist(struct DNode *plist)
{
	assert(plist != NULL);
	if(NULL == plist)
		return;

	//plist->data; 头结点的数据域不需要赋值
	plist->next = NULL;
	plist->prior = NULL;
}

//头插
bool InsertHead(PDNode plist, int val)
{
	assert(plist != NULL);
	//1.创建新节点
	DNode *pnewnode = (DNode*)malloc(sizeof(DNode) * 1);
	assert(pnewnode != NULL);
	pnewnode->data = val;

	//2.找到合适的插入位置   头插

	//3.插入  3421
	pnewnode->next = plist->next;//自身的next
	pnewnode->prior = plist;//自身的priro
	if(pnewnode->next != NULL)//保证下一个节点存在
	{//下一个节点的前驱指针
		pnewnode->next->prior = pnewnode;//plist->next->prior = pnewnode; 
	}
	plist->next = pnewnode;//上一个节点的后继指针

	return true;
	/*
	node->next = plist->next;
	pnewnode->prior = plist;
	plist->next = pnewnode;
	if(pnewnode->next!=NULL)
	{
		pnewnode->next->prior = pnewnode;// 区别:和上面比,(这快这行代码失效:plist->next->prior = pnewnode;)  因为plist->next 指向已经修改了
	}
	*/
}

//尾插
bool InsertTail(PDNode plist, int val)
{
	assert(plist != NULL);
	DNode *pnewnode = (DNode*)malloc(sizeof(DNode) * 1);
	assert(pnewnode != NULL);
	pnewnode->data = val;

	DNode *p = plist;
	for(p; p->next!=NULL; p=p->next);

	pnewnode->next = p->next;
	pnewnode->prior = p;
	//因为是尾插操作  所以下一个节点的前驱不需要修改
	p->next = pnewnode;

	return true;
}

//按位置插
bool InsertPos(PDNode plist, int pos, int val)
{
	assert(plist != NULL&&pos>=0&&pos<=Get_length(plist));
	//将只需要处理三条线的特殊情况 提前处理掉
	if(pos == 0)//如果pos为0   并且plist是个空链 则只需要改变三条线
	{
		return InsertHead(plist, val);
	}
	if(pos == Get_length(plist))//如果pos ==length  则一定第三条线不存在
	{
		return InsertTail(plist, val);
	}

	DNode *pnewnode = (DNode*)malloc(sizeof(DNode) * 1);
	assert(pnewnode != NULL);
	pnewnode->data = val;
	
	DNode *p = plist; 
	for(int i=0; i<pos; i++)
	{
		p=p->next;
	}

	pnewnode->next = p->next;
	pnewnode->prior = p;
	pnewnode->next->prior = pnewnode;
	p->next = pnewnode;
	return true;
}


//头删
bool DelHead(PDNode plist)
{
	assert(plist != NULL);
	if(IsEmpty(plist))
		return false;

	DNode *q = plist->next;
	plist->next = q->next;//修改上一个节点的后继
	if(q->next != NULL)//保证待删除节点后面的节点存在
	{
		q->next->prior = plist;//修改下一个节点的前驱
	}

	free(q);
	return true;
}

//尾删
bool DelTail(PDNode plist)
{
	assert(plist != NULL);
	if(IsEmpty(plist))
		return false;

	DNode *q = plist;
	for(q; q->next!=NULL; q=q->next);
	DNode *p = q->prior;

	p->next = q->next;// p->next = NULL;
	//下一个节点的前驱 不需要修改  因为是尾删   q已经指向的是最后一个节点了
	return true;
}

//按位置删
bool DelPos(PDNode plist, int pos)
{
	assert(plist!=NULL && pos>=0 && pos<Get_length(plist));

	if(pos == 0)
	{
		return DelHead(plist);
	}
	if(pos == Get_length(plist)-1)
	{
		return DelTail(plist);
	}

	DNode *p = plist;
	for(int i=0; i<pos; i++)
	{
		p=p->next;
	}
	DNode *q = p->next;

	p->next = q->next;//上一个节点的next
	q->next->prior = p;//下一个节点的prior
	free(q);
	return true;
}

//按值删
bool DelVal(PDNode plist, int val)
{
	
	DNode *q = plist->next;
	for(q; q!=NULL; q=q->next)
	{
		if(q->data == val)
		{
			DNode *p = q->prior;
			p->next = q->next;
			if(q->next != NULL)
			{
				q->next->prior = p;
			}
			free(q);
			return true;
		}
	}

	return false;
}

//查找
struct DNode *Search(PDNode plist, int val)
{
	DNode *p = plist->next;
	for(p; p!=NULL; p=p->next)
	{
		if(p->data == val)
		{
			return p;
		}
	}
	return NULL;
}

//判空
bool IsEmpty(PDNode plist)
{
	return plist->next == NULL;
}

//获取有效元素个数
int Get_length(PDNode plist)
{
	int count = 0;
	DNode *p = plist->next;
	for(p; p!=NULL; p=p->next)
	{
		count++;
	}
	return count;
}

//打印
void Show(PDNode plist)
{
	DNode *p = plist->next;
	for(p; p!=NULL; p=p->next)
	{
		printf("%d ", p->data);
	}
	printf("\n");
}

//清空
void Clear(PDNode plist)
{
	Destroy1(plist);
}

//销毁
void Destroy1(PDNode plist)
{
	while(!IsEmpty(plist))
	{
		DelHead(plist);
	}

	plist->next = NULL;
}
void Destroy2(PDNode plist)
{
	DNode *p = plist->next;
	DNode *q;

	while(p!=NULL)
	{
		q = p->next;
		free(p);
		p = q;
	}

	plist->next = NULL;
}

7.(单)循环链表

图5

易错点:

1)尾节点的next指向的是头结点,而不是第一个有效节点

2)单循环链表只能从尾巴找到头,而不能直接从头找到尾巴

3)for循环向后跑的时候,循环结束条件不再是和NULL比较,而是和头结点的地址比较

两种for循环

1>for(CNode* p=plist;p!=plist;p=p->next)

2>for(CNode* p=plist->next;p!=plist;p=p->next)

clist.h

#pragma once

//带头结点的单链表的结构体设计:
typedef int ELEMTYPE;
typedef struct CNode
{
	ELEMTYPE data;//数据域  保存节点有效值
	struct Node *next;//指针域 指向下一个节点的地址
}Node, *PCNode;



//可以实现的操作 声明
//增删改查

//初始化函数(给头结点赋值)
void Init_clist(struct CNode* plist);

//头插
bool Insert_Head(PCNode plist, int val);

//尾插
bool Insert_Tail(PCNode plist, int val);

//按位置插  pos
bool Insert_pos(PCNode plist, int pos, int val);

//头删
bool Del_Head(PCNode plist);

//尾删
bool Del_Tail(PCNode plist);

//按位置删 pos
bool Del_Pos(PCNode plist, int pos);

//按值删(将值为val的第一个节点删除掉)
bool Del_val(PCNode plist, int val);

//查找 val 在plist这个单链表中找值为val的第一个节点   找到后返回其地址  不然返回NULL
struct CNode * Search(PCNode plist, int val);

//判空
bool IsEmpty(PCNode plist);

//判满 单链表不需要判满

//获取前驱 (将值为val的节点去前一个节点返回)
struct CNode* Get_Prior(PCNode plist, int val);

//获取后继 (将值为val的节点去后一个节点返回)
struct CNode* Get_Next(PCNode plist, int val);

//获取其有效节点个数
int Get_Length(PCNode plist);

//清空
void Clear(PCNode plist);

//销毁1
void Destroy1(PCNode plist);
//销毁2
void Destroy2(PCNode plist);

void Show(PCNode plist);

clist.cpp

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "clist.h"


//初始化函数(给头结点赋值)
void Init_clist(PCNode plist)
{
	assert(plist != NULL);
	if(plist == NULL)
	{
		return;
	}

	//plist->data;  头结点的数据域不使用
	plist->next = plist;
}

//头插
bool Insert_Head(PCNode plist, int val)
{
	assert(plist!=nullptr);

	//1.创建一个新节点
	CNode * pnewnode = (CNode*)malloc(sizeof(CNode) * 1);//动态内存申请一个新节点
	assert(pnewnode != NULL);
	pnewnode->data = val;
	pnewnode->next = NULL;//此行代码 可加可不加 因为紧接着就会对pnewnode->next 重新赋值

	//2.找到合适的插入位置(头插函数  这一步不需要处理)

	//3.插入新节点  1和2 顺序不能颠倒   
	pnewnode->next = plist->next;//1
	plist->next = pnewnode;//2

	return true;
}

//尾插
bool Insert_Tail(PCNode plist, int val)
{
	assert(plist!=nullptr);

	//1.创建新节点
	CNode* pnewnode = (CNode)malloc(sizeof(CNode) * 1);
	assert(pnewnode != NULL);
	pnewnode->data = val;
	
    //2.先找到最后一个节点
	CNode *p=plist;
	for(p; p->next!=plist; p=p->next);  //此时for循环结束 p指向的是尾结点

    //3.给它后面插入
	pnewnode->next = p->next;
	p->next = pnewnode;
	return true;
}

//按位置插  pos
bool Insert_pos(PCNode plist, int pos, int val)
{
	assert(plist!=NULL && pos>=0 && pos<=Get_Length(plist));
	if(plist == NULL || pos<0 || pos>Get_Length(plist))
		return false;

	//1.创建新节点
	CNode *pnewnode = (CNode)malloc(sizeof(CNode) * 1);
	assert(pnewnode != NULL);
	pnewnode->data = val;

	//2.找到合适的插入位置
	CNode* p = plist;
	for(int i=0; i<pos; i++)
	{
		p=p->next;
	}
	//此时 p就指向待插入位置的前驱

	//3.给它后面插入
	pnewnode->next = p->next;
	p->next = pnewnode;

	return true;
}

//头删
bool Del_Head(PCNode plist)
{
	assert(plist!=nullptr);
	if(IsEmpty(plist))
		return false;

	//1.申请一个临时变量指向要删除的第一个有效节点
	CNode *p = plist->next;

	//2.将要删除的节点左右连接起来
	plist->next  = p->next;//plist->next = plist->next->next;

	//3.将要删除的节点内存释放,防止内存泄露
	free(p);
	p = NULL;

	return true;
}

//尾删
bool Del_Tail(PNode plist)
{
	assert(plist!=nullptr);

	//1.先找到要删除节点的上一个节点(前驱)
	CNode *p =plist;
	for(p; p->next->next!=plist; p=p->next);
	//此时p就指向倒数第二个节点

	//2.申请一个临时变量指向要删除的有效节点
	CNode *q = p->next;

	//3.跨越指向(将要删除的节点左右连接起来)
	p->next = q->next;//p->next = p->next->next;

	//4.释放内存
	free(q);
	q=NULL;
	return true;
}

//按位置删 pos
bool Del_Pos(PCNode plist, int pos)
{
	assert(plist!=NULL && pos>=0 && pos<Get_Length(plist));
	if(plist==NULL || pos<0 || pos>=Get_Length(plist))
		return false;

	
	//1.找到删除节点的上一个节点
	CNode *p = plist;
	for(int i=0; i<pos; i++)
	{
		p = p->next;
	}
	//此时p就指向pos指向节点的前一个节点

	//2.申请一个临时变量指向要删除的有效节点
	CNode *q = p->next;

	//3.跨越指向(将要删除的节点左右连接起来)
	p->next = q->next;

	//4.释放内存
	free(q);

	return true;
}

//按值删(将值为val的第一个节点删除掉)
bool Del_val(PCNode plist, int val)
{
	assert(plist!=nulptr);
	CNode *p = Get_Prior(plist, val);
	if(p==plist)
	{
		return false;
	}

	CNode *q = p->next;
	p->next = q->next;
	free(q);
	q = NULL;

	return true;

}

//查找 val 在plist这个单链表中找值为val的第一个节点   找到后返回其地址  不然返回NULL
struct CNode * Search(PCNode plist, int val)
{
	//不需要前驱的操作  所以 用第二种for循环
	assert(plist!=nullptr);
	CNode *p = plist->next;
	for(p; p!=plist; p=p->next)
	{
		if(p->data == val)
		{
			return p;
		}
	}
	return NULL;
}

//判空
bool IsEmpty(PCNode plist)
{
	return plist->next == plist;
}

//判满 单链表不需要判满

//获取前驱 (将值为val的节点去前一个节点返回)
struct CNode* Get_Prior(PCNode plist, int val)
{
	assert(plist!=nullptr);
	CNode *p=plist;
	for(p; p->next!=plist; p=p->next)
	{
		if(p->next->data == val)
		{
			return p;
		}
	}

	return NULL;
}

//获取后继 (将值为val的节点去后一个节点返回)
struct CNode* Get_Next(PCNode plist, int val)
{
	CNode *p = Search(plist, val);
	if(p==plist)
	{
		return NULL;
	}

	return p->next;
}

//获取其有效节点个数
int Get_Length(PCNode plist)
{
	assert(plist!=nullptr);
	int count = 0;
	CNode *p = plist->next;
	for(p; p!=plist; p=p->next)
	{
		count++;
	}

	return count;
}

//清空
void Clear(PCNode plist)
{
	Destroy1(plist);
}

//销毁1   一致头删   这个一定掌握(只用了一个指针)
void Destroy1(PCNode plist)
{
	assert(plist!=nullptr);
	/*while(!IsEmpty(plist))
	{
		Del_Head(plist);
	}*/

	while(plist->next != plist)
	{
		CNode *p = plist->next;
		plist->next = p->next;
		free(p);
		p = NULL;
	}
}

//销毁2  不借助头节点(两个指针)  能理解就理解 理解不了记住第一种头删的方式即可
void Destroy2(PCNode plist)
{
	assert(plist!=nullptr);
	CNode *p = plist->next;
	CNode *q;

	while(p != plist)
	{
		q = p->next;
		free(p);
		p = q;
	}

	plist->next = plist;
}

void Show(PCNode plist)
{
	assert(plist!=nullptr);
	CNode *p = plist->next;
	for(p; p!=plist; p=p->next)
	{
		printf("%d ", p->data);
	}

	printf("\n");
}

8.例题

1>单链表如何逆置

图6
图7

2>

(1)如何判断两个单链表是否存在交点

图8

(2)找出相交首节点

图9

3>删除p某节点(p某一定不能是尾节点)

图10

4>如何判断一个单链表是否存在一个环,如果存在一个环,将入环点返回

图11
图12

#include"list.h"
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<math.h>

//1。单链表如何逆置
//一直头插
void Reverse1(PNode plist)
{
    assert(plist!=nullptr);
    Node* p=plist->next;
    Node* q=nullptr;
    plist->next=nullptr;
    
    while(p!=plist)
    {
        q=p->next;
        p->next=plist->next;
        plist->next=p;
        p=q;
    }
}
//不借助头结点,用三个指针
void Reverse2(PNode plist)
{
    assert(plist!=nullptr&&plist->next!=nullptr);
    Node* p=plist->next;
    Node* q=p->next;
    Node* r=nullptr;
    p->next=nullptr;
    
    while(q!=nullptr)
    {
        r=q->next;
        q->next=p;
        p=q;
       	q=r;
    }
    plist->next=p;
}

//2.如何判断两个单链表是否存在交点    相交节点只有一个next域,也就是说相交节点后面只有一根线
bool Intersect(PNode plist1,PNode plist2)
{
    assert(plist1!=nullptr&&plist2!=nullptr);
    Node* p=plist1;
    Node* q=plist2;
    
    for(p;p->next!=nullptr;p=p->next);
    for(q;q->next!=nullptr;q=q->next);
    return p==q?true:false;
    /*if(p==q)
    {
        return true;
    }
    return false;*/
}
//找出相交首节点
//让长的单链表先走几步,保证两条单链表长度相等,然后同步比较
Node* Intersect_get_first(PNode plist1,PNode plist2)
{
	assert(plist1!=nullptr&&plist2!=nullptr);
    int len1=Get_Length(plist1);
    int len2=Get_Length(plist2);
    Node* p=len1>=len2?plist1:plist2;
    Node* q=len1>=len2?plist2:plist2;
    for(int i=0;i<abs(len1-len2);i++)//ads()是取绝对值函数
    {
		p=p->next;
    }
    while(p!=nullptr)
    {
        if(p==q)
        {
			return p;//return q;
        }
        p=p>next;
        q=q->next;
    }
    return NULL;
}

//3.删除p节点(p一定不能是尾节点)
bool Del_Node(PNode plist,PNode p)
{
    assert(plist!=nullptr);
    if(p->next==nullptr)
    {
		return Del_Tail(plist);
        //return false;
    }
    p->data=p>next->data;
    Node* q=p->next;
    p->next=q->next;//换脸术//重要的数据,节点还可以再向系统申请
    free(q);
    return true;
}

//4.如何判断一个单链表是否存在一个环,如果存在一个环,将入环点返回
//追击问题
Node* IsCircle(PNode plist)
{
	assert(plist!=nullptr&&plist->next!=nullptr);
    Node* fast=plist->next->next;
    Node* slow=plist->next;
    
    while(fast!=nullptr&&fast!=slow)//左边没有环   右边代表有环且相遇
    {
		slow=slow->next;
        fast=fast->next;
        if(fast==nullptr)
        {
            return false;
        }
        fast=fast->next;
    }
    if(fast==nullptr)
    {
        return NULL;
    }
   //此时单链表有环
   /* 
    头结点到入环点距离为x,入环点到相遇点距离为y,相遇点到入环点(剩下的弧形)距离为z
    slow=x+y      fast=x+y+n(y+z)
    2(x+y)=x+y+n(y+z)  =>   x=(n-1)(y+z)+z
    让一个指针从头节点开始跑,让另一个指针以同样的速度从相遇点开始跑,最后相遇就是入环点
   */
    Node* p=plist;
    Node* q=fast;
    while(p!=q)
    {
        p=p->next;
        q=q->next;
    }
    return q;//入环点
    //return p;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值