考研数据结构——(线性表_单链表)

定义需要

#include <stdio.h>
#include <stdlib.h>

/**
	定义单链表的结构体
*/

typedef struct LNode {
	int data;
	LNode* next;
} LNode, *LinkList;

1. 头插法初始化链表(带头结点)

  • 首先L表示的就是头结点,而指向L的过程是头指针
  • 为L即头结点开辟空间,初始L->next为 NULL:表示是空表。
  • 创建一个临时结点node,用于插入
  • 通过循环赋值的方式为链表初始化,我这采用的方式为先输入一个值,后开辟空间,这样判断不再增加结点的时候,就不用开辟空间了。
  • ⭐ 头插法的思路:
  1. 创建一个新的临时结点(node), 先让这个临时结点指向第一个元素(L->next)。 即 node->next = L->next;
  2. 让头结点指向这个新创建的临时结点: L->next = node;
/**
	1.  头插法初始化(带头结点单链表) 
*/

void linkListHeadInsert(LinkList &L) {
	// 先创建一个临时结点s 
	LNode* node;
	int x;
	// 为头结点开辟空间 
	L = (LinkList)malloc(sizeof(LNode));
	// 头结点初始化指向为 NULL 
	L->next = NULL;
	// 头结点初始值为 0 
	L->data = 0; 
	
	
	while(true) {
		
		// 为新点击赋值 
		printf("请为新生成的结点赋值,如果不生成请输入-1\n");
		scanf("%d", &x);
		if(x==-1) {
			// 如果x=-1,就不生成了,也就不用开辟新的空间了
			break; 
		} 
		// 为新结点开辟空间 
		node = (LinkList)malloc(sizeof(LNode));
		
		if(node == NULL) {
			// 如果地址开辟失败
			printf("地址开辟失败\n");
			break; 
		}
		
		// 为新结点赋值 
		node->data = x;
		
		// 新的结点指向原来的第一个元素(即头结点指向的)
		node->next = L->next;
		// 头结点指向新结点
		L->next = node; 
	} 
} 

2. 尾插法初始化链表

  • 先为头结点开辟空间
  • 创建一个临时的node,用于生成新的结点
  • ⭐尾插法的关键:
  1. 要有一个始终指向尾部的一个结点end, 初始化的时候 end=L
  2. 具体的插入逻辑, 就是尾部结点end指向新插入的结点node即 end->next = node;
    新插入的结点就是尾部了, 所以 end = node;
    注意: 一定注意为node->next设置为NULL,或者在循环结束后给 end->next = NULL; 不然会死循环
/**
	2.  尾插法初始化(带头结点单链表) 
*/
void linkListEndInsert(LinkList &L) {
	// 为头结点开辟一个空间
	L = (LinkList)malloc(sizeof(LNode));
	// 初始化头结点
	L->data = 0;
	L->next = NULL; 
	
	// 创建临时结点
	LNode* node;
	int x;
	
	// 需要创建一个始终指向尾部的指针 ,  初始就是L 
	LNode* end = L;
	
	while(true) {
		// 为新点击赋值 
		printf("请为新生成的结点赋值,如果不生成请输入-1\n");
		scanf("%d", &x);
		if(x==-1) {
			// 如果x=-1,就不生成了,也就不用开辟新的空间了
			break; 
		} 
		
		node = (LinkList)malloc(sizeof(LNode));
		node->data = x;
		// 因为新的结点要插入到尾部,所以每个都是最后一个,最后一个的next是null
		// 这个最后一个指向为null也可以设置到循环外 
		node->next = NULL; 
			
		// 尾部插入的逻辑
		// 因为end表示最后一个元素, 新的元素插入到它的next 
		end->next = node;
		// 新插入的元素就是 最后一个了  即end 
		end = node; 
		
	}	 	 
} 

3. 遍历链表

  • 结点不为空,继续循环输出
  • 每次输出后,指向后一个结点
/**
	3.  循环遍历单链表
*/

void linkListPrint(LinkList L) {
	// L指向过程是头指针, L是头结点
	// 拿到第一个元素即,头结点指向的第一个元素 
	
	// 因为是带头结点的单链表, 所以先拿到头结点指向的第一个元素 
	LNode* node = L->next;
	
	// 结点值不为空的时候,继续向下遍历(判断条件是NULL) 
	printf("链表的遍历:");
	while(node != NULL) {
		 printf("->>>%d ", node->data);
		 // node指向下一个结点
		 node = node->next; 
	}
	printf("\n");
	
} 

4. 查找元素,返回结点地址,否则返回NULL

  • 首先将头指针指向的,也就是第一个元素赋值给一个临时的结点变量node
  • 通过while循环判断,该结点是否为NULL; 因为while是先判断后运行, 如果第一个元素就为空, 直接就执行到最后对的未找到语句。
  • 每次判断当前结点值是否为要找到的值,如果是,就返回节点值。如果不是就继续往下循环。
/**
	4.  查找元素, 将找到的节点返回。 
*/
LNode* getElem(LinkList L, int el) {
	// 第一个元素
	LNode* node = L->next; 
	
	// 开始循环查找 
	while(node!=NULL) {
		if(node->data == el) {
			// 找到了该元素,返回该结点
			return node; 
		}
		
		// 如果本结点不是
		node = node->next; 
	}
	
	// 如果循环结束没找到
	printf("\n\n没找到该节点\n\n");
	return NULL; 
	
}

5. 在链表第i个位置插入元素

  • 首先将头结点赋值给一个循环的结点变量nodeNext
  • 循环从1开始, 到i-1结束。其实就是循环 i-1次,⭐找到要插入位置的前一个结点的地址。⭐在循环的过程中要判断结点是否为NULL,如果产生为NULL,说明插入位置溢出了。
  • 考虑一下边界条件 第一个位置与最后一个位置
  1. 1也就是插入到位置最开始, 此时循环并不会执行, 我们的nodeNext就是第一个元素前的元素(头结点)
  2. 考虑最后一个位置, 最后一个位置前的元素就是最后一个元素,也不会为NULL
  3. 所以循环的判断方案适合于 插入元素到第一个位置到最后一个位置的任何一个位置
  • 插入的思想⭐
  1. 循环遍历结束后 nodeNext是要插入位置的前一个元素。
  2. 首先将插入位置的元素也就是 nodeNext->next赋值给node的next 即 node->next = nodeNext->netx , 防止找不到。
  3. 再将nodeNext->next = node; (新插入的元素放在插入位置的后面)
  4. 所以 结构为 nodeNext >> 新结点 >> 原来插入位置的结点
/**
	5.  在链表的第i个位置插入元素 
*/

void linkListInsertByIndex(LinkList &L, int i) {
	// 用于往下循环的节点值
	LNode* nodeNext = L; 
	
	// 创建一个插入的结点
	LNode* node;
	int e;
	int j;
	for(j=1 ; j<i; j++) {
		// 我要遍历到插入位置的前一个
		// 例如我要插入到第3个位置,我此时跑2次,  也就是我从头结点向后移动两次, 为第二个元素  
		if(nodeNext == NULL) {
			// 如果在nodeT 在向后寻找的过程中就出现NULL,说明插入的位置溢出了
			printf("\n\n  插入位置不正确  \n\n");
			return; 
		}
		
		nodeNext = nodeNext->next;
	}
	printf("请输入要插入的值:");
	scanf("%d", &e);
	// 为node开辟空间
	node = (LinkList)malloc(sizeof(LNode));
	if(node == NULL) {
		printf("\n\n  开辟空间失败  \n\n");
		return;
	}
	node->data = e;
	// 首先新的结点指向我上面插入位置前一个元素所指向的元素
	node->next = nodeNext->next;
	// 前一个元素指向新的元素
	nodeNext->next = node; 
	printf("插入成功!\n\n");
} 

6. 删除指定位置的结点

  • 删除的逻辑和插入一样,首先要先找到 要删除元素的前一个
  • ⭐但是在边界控制条件上, 插入可以插入到最后一个元素后, 但是删除只能删除第一个元素到最后一个元素的位置, 所以判断的条件加一个 nodeNext->next == NULL(满足条件就是不合适删除的位置)
  • 删除的逻辑
  1. 删除位置前的结点直接指向删除位置后的结点
  2. 释放掉要删除的结点
/**
	6.  删除第i个位置的结点 
*/

void linkListDelByIndex(LinkList &L, int i) {
	// 创建一个循环往下的临时结点
	LNode* nodeNext = L; 
	int j; 
	for(j=1 ; j<i; j++) {
		// 我要遍历到删除位置的前一个
		// 例如我要插入到第3个位置,我此时跑2次,  也就是我从头结点向后移动两次, 为第二个元素 	
		// 删除逻辑, 元素在向后移动的时候,判断条件多了  nodeNext->next是否为空
		// 这里的目的为了  限制最后一个元素的后一个位置(假如有七个元素,  插入位置为8可以,但是删除位置为8不是正确逻辑)
		// (当有七个元素删除第8个的时候,  循环次数为 7,  所以要判断一下 下一个元素是否为NULL, 为NULL表示后面没有元素可以删除了) 
		if(nodeNext == NULL || nodeNext->next==NULL) {
			// 如果在nodeT 在向后寻找的过程中就出现NULL,说明插入的位置溢出了
			printf("\n\n  删除位置不正确  \n\n");
			return; 
		}
		
		nodeNext = nodeNext->next;
	} 
	// 具体的删除逻辑
	// 先将要删除的元素地址存下, 方便在删除后释放 
	LNode* nodeT = nodeNext->next; 
	
	// 将删除元素的前一个元素 直接指向 删除位置的下一个元素
	// (这样即使删除的是最后一个元素,  删除元素位置前的元素最终指向的是  NULL,  她便成了最后一个元素)
	nodeNext->next = nodeT->next; 
	
	// 释放删除了元素的空间
	free(nodeT); 
	
} 

7. 求链表的长度

  • 通过循环计数统计
  • 初始赋值第一个元素为nodeNext
  • 每次循环向后移动,直到nodeNext为NULL
/**
	7. 求表长
	
*/

int getLinkListLen(LinkList L) {
	// 设置一个用于循环的结点
	LNode* nodeNext = L->next;
	// 计数器
	int count=0; 
	while(nodeNext!=NULL) {
		// 第一次能进来说明, L(头结点有指向)
		
		count++;
		
		// 每次往后移动一次
		nodeNext=nodeNext->next; 
	} 
	// 返回总长度
	return count; 
} 

未完待续…

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值