C 链表  

链表ADT操作的代码实现

struct list *create_list()//创建一个节点,返回值为list结构体指针
void traverse(struct list *ls)			//遍历链表
struct list *insert_list(struct list *ls,int n,int data)	//在指定位置插入元素,(首节点,在第几个节点插入,这个节点的data值)
int delete_list(struct list *ls,int n)		//删除链表元素,1链表首元素,要删除第几个?
int count_list(struct list *ls)		//返回链表元素个数,包括首节点
void  clear_list(struct list *ls)	//清空链表,保留首节点
int empty_list(struct list *ls)	//返回链表是否为空
struct list *local_list(struct list *ls,int n)		//返回链表对应的第N个节点的地址
struct list *elem_local(struct list *ls,int data)	//返回链表中数据域值data的节点地址
int elem_pos(struct list *ls,int data)		//返回链表中数据域值data的节点的序号
struct list *last_list(struct list *ls)		//返回链表的最后一个元素
void merge_list(struct list *ls1,struct list *ls2)	//把链表2的有效元素合并到链表1
void reverse_list(struct list *ls)                      //逆置链表

[email protected]:~/list$ cat main.c 
//单向链表
#include <stdio.h>
#include <stdlib.h>
 
struct list //链表结构体
{
	int data;		// 数据
	struct list *next;	//指针域,指向一下个域的指针
};

struct list *create_list()//创建一个节点,返回值为list结构体指针
{
	//方法1
	//struct list *p = (struct list *)malloc(sizeof(struct list));
	//p -> data = 0;
	//p -> next = NULL;
	//return p;

	// 方法2calloc 会自动清空内存
	return calloc(sizeof(struct list),1);
}

void traverse(struct list *ls)			//遍历链表
{
	// 方法1 
	//int i = 0;
	//struct list *p = ls;			//从给的这个节点开始遍历
	//printf("首节点的地址 %p,本节点的值 %d ,下一个节点的地址 %p\n",p,p->data,p->next);
	//p = p->next;

	//while(p)
	//{
	//	printf("节点%d的地址 %p,本节点的值 %d ,下一个节点的地址 %p\n",i,p,p->data,p->next);
	//	p = p->next;
	//	i++;
	//}

	//方法2【递归】
	if(ls)		//只要地址不为NULL
	{
		//先序递归
		printf("地址 %p,本节点的值 %d \n",ls,ls->data);
		traverse(ls->next);

	}
}

struct list *insert_list(struct list *ls,int n,int data)	//在指定位置插入元素,(首节点,在第几个节点插入,这个节点的data值)
{
	struct list *p = ls;
	while(p && n--)
	{
		p = p->next;
	}
	if(!p) return NULL;//n指向了一个无效的位置
	else
	{
		struct list *node = create_list();
		node->data = data;
		//掐断原来的链表,重新组合
		node->next = p->next;
		p->next    = node;
		return node;
	}
}
int delete_list(struct list *ls,int n)		//删除链表元素,1链表首元素,要删除第几个?
{
	struct list *p = ls;
	while(p && n--)
	{
		p = p->next;
	}
	if(!p) return 0;//n指向了一个无效的位置
	else
	{
		struct list *node = p->next;	//记录待删除节点的地址
		if(!node) return 0;		//如果这个节点是无效的,就返回无效
		p->next = node->next;		//将该节点next指向待删除节点的next节点的next节点
		free(node);
		return 1;			//操作成功!
	}

}

int count_list(struct list *ls)		//返回链表元素个数,包括首节点
{
	struct list *p = ls->next;
	int count = 0;
	while(p)
	{
		count++;
		p = p->next;
	}
	return count;
}

void  clear_list(struct list *ls)	//清空链表,保留首节点
{
	struct list *p   = ls->next;	//要跳过首节点,进入该链表的第一个有效元素
	struct list *tmp;
	while(p)
	{
		tmp = p->next;		//记录当前节点的next
		//p->next = NULL;	//没必要的操作
		free(p);		//释放这个有效的元素
		p = tmp;
	}
	ls->next = NULL;		//链表清空后,首节点就是尾节点,一定设置为NULL
}

int empty_list(struct list *ls)	//返回链表是否为空
{
	if(ls->next)		//如果不为空,返回1
		return 1;
	else			//如果  为空,返回0
		return 0;
}
struct list *local_list(struct list *ls,int n)		//返回链表对应的第N个节点的地址
{
	struct list *p = ls->next;			//立即指向首节点的下一个元素
	while(p && n--)
	{
		p = p->next;
	}
	return p;
}

struct list *elem_local(struct list *ls,int data)	//返回链表中数据域值data的节点地址
{
	struct list *p = ls->next;			//立即指向首节点的下一个元素
	while(p)
	{
		if(p ->data == data) {break;}
		p = p->next;
	}
	return p;
}

int elem_pos(struct list *ls,int data)		//返回链表中数据域值data的节点的序号
{
	struct list *p = ls->next;			//立即指向首节点的下一个元素
	int count = 0;
	while(p)
	{
		if(p ->data == data) {break;}
		p = p->next;
		count++;
	}
	return count;
}

struct list *last_list(struct list *ls)		//返回链表的最后一个元素
{
	struct list *p  =ls->next;
	struct list *back = p;
	while(p)
	{
		back = p;
		p = p->next;
	}
	return back;
}
void merge_list(struct list *ls1,struct list *ls2)	//把链表2的有效元素合并到链表1
{
	struct list *last = last_list(ls1);
	last->next = ls2->next;
}
void reverse_list(struct list *ls)
{
	if(!(ls || ls->next || ls->next->next)) {return ;}	//只有大于1个有效节点,才需要逆置
	struct list *left   = NULL;				//记录上一个节点的地址,初始值的NULL刚好可以作为尾节点的next的NULL
	struct list *curr   = ls->next;				//记录当前节点的地址
	struct list *right  = NULL;				//遍历完原链表的所以元素
	while(curr)						//遍历完原链表的所有元素
	{
		right      = curr->next;			//下一个节点就是当前节点的next
		curr->next = left;				//第一次执行的时候,curr节点就是尾节点了,就将尾节点的next指向了NULL
		left       = curr;				//把当前的节点作为上一个节点
		curr       = right;				//把下一个节点作为当前节点
	}
	ls->next = left;					//原链表的最后的一个有效元素就已经变为left了,让链表节点指向这个left就好了。
}

int main()
{
	//链表操作,入门
	{
		struct list *first  = create_list();	//创建一个节点,为首节点
		struct list *second = create_list();	//再创建一个节点
		struct list *thrid  = create_list();	//再创建一个节点
		first  ->next = second;			//第一个指向第二个
		second ->next = thrid;			//第二个指向第三
		first  ->data = 1	;		//为节点设置数值
		second ->data = 2;
		thrid  ->data = 3;
		//traverse(first);

		//如果最后一个节点的指向不是NULL会怎样
		free(thrid);
		//second ->next = NULL;
		traverse(first);			//不可预期的结果

		//如何删除第二个节点?
		free(second);
		first  ->next = thrid;			//第一个指向第三个
	}

	/*	链表ADT操作的代码实现		*/
	printf("\n\n\n开始批量创建链表\n");
	struct list *node_start = create_list();	//此时链表的长度只有1个,有效元素个数为0
	for(int i=0;i<4;i++)				//在原来的首节点上插入10个,结果就是11个
	{
		//链表插入只能在首节点之后
		//insert_list(node_start,i,i+100);	//每次都在第i个位置插入,越后来插入的越靠后
		insert_list(node_start,0,i+100);	//每次都在第0个位置插入,越后来插入的越靠前
	}

	delete_list(node_start,0);			//在指定位置删除节点
	insert_list(node_start,2,666);			//在指定位置插入节点
	traverse(node_start);				//遍历链表
	printf("链表有效元素有%d个(不包括首节点)\n",count_list(node_start));	//返回这个链表元素的个数
	printf("第2个节点的数据是%d\n",local_list(node_start,2)->data);		//返回链表第N个有效元素的地址
	printf("最后一个元素的数据域是 %d\n",last_list(node_start)->data);	//返回链表 最后一个元素的地址

	printf("\n\n链表合并,结果输出\n");
	struct list *node2 = create_list();
	for(int i=0;i<8;i++)
	{
		insert_list(node2,0,i+33);
	}
	merge_list(node_start,node2);			//把node2合并到node_start
	traverse(node_start);				//遍历链表

	printf("\n逆置链表,结果输出\n");
	reverse_list(node_start);
	traverse(node_start);				//遍历链表

	printf("\n\n\n清空链表\n");
	clear_list(node_start);				//清空链表
	traverse(node_start);				//遍历链表

	return 0;
}
[email protected]:~/list$

编译运行:

[email protected]:~/list$  gcc  -std=c99 main.c && ./a.out 
地址 0x1ce2010,本节点的值 1 
地址 0x1ce2030,本节点的值 2 
地址 0x1ce2050,本节点的值 0 

开始批量创建链表
地址 0x1ce2030,本节点的值 0 
地址 0x1ce2090,本节点的值 102 
地址 0x1ce2070,本节点的值 101 
地址 0x1ce20b0,本节点的值 666 
地址 0x1ce2050,本节点的值 100 
链表有效元素有4个(不包括首节点)
第2个节点的数据是666
最后一个元素的数据域是 100

链表合并,结果输出
地址 0x1ce2030,本节点的值 0 
地址 0x1ce2090,本节点的值 102 
地址 0x1ce2070,本节点的值 101 
地址 0x1ce20b0,本节点的值 666 
地址 0x1ce2050,本节点的值 100 
地址 0x1ce21d0,本节点的值 40 
地址 0x1ce21b0,本节点的值 39 
地址 0x1ce2190,本节点的值 38 
地址 0x1ce2170,本节点的值 37 
地址 0x1ce2150,本节点的值 36 
地址 0x1ce2130,本节点的值 35 
地址 0x1ce2110,本节点的值 34 
地址 0x1ce20f0,本节点的值 33 

逆置链表,结果输出
地址 0x1ce2030,本节点的值 0 
地址 0x1ce20f0,本节点的值 33 
地址 0x1ce2110,本节点的值 34 
地址 0x1ce2130,本节点的值 35 
地址 0x1ce2150,本节点的值 36 
地址 0x1ce2170,本节点的值 37 
地址 0x1ce2190,本节点的值 38 
地址 0x1ce21b0,本节点的值 39 
地址 0x1ce21d0,本节点的值 40 
地址 0x1ce2050,本节点的值 100 
地址 0x1ce20b0,本节点的值 666 
地址 0x1ce2070,本节点的值 101 
地址 0x1ce2090,本节点的值 102 

清空链表
地址 0x1ce2030,本节点的值 0 
[email protected]:~/list$

C 链表

时间: 2024-10-21 15:26:16

C 链表  的相关文章

c语言动态链表的创建

创建动态连链表就是将一个个节点连接起来 (1)动态生成节点 (2)输入节点数据 (3)将节点链在一起 例: typedef struct Data { char num[20]; char name[10]; char sex; float english; float chinese; float math; }; typedef struct Node { struct Data data;//结构体类型//结构体嵌套 struct Node* next;//结构体指针型 }node,*Pn

算法学习——单链表快排

/**  * 以p为轴对start-end间的节点进行快排(包括start && 不包括end):  * 思路:  * 1.将头节点作为轴节点start,从start.next开始遍历,如果节点小于轴start的值,将该节点插入到轴节点后面:  * 2.将轴节点插入合适位置,即找到最后一个小于轴的节点,将该节点与轴节点值互换,此时就链表分为两部分,小于轴节点和大于轴节点:  * 3.递归的遍历2中两部分节点.  *   * @param p  * @param start  * @para

【数据结构】之散列链表(Java语言描述)

散列链表,在JDK中的API实现是 HashMap 类. 为什么HashMap被称为"散列链表"?这与HashMap的内部存储结构有关.下面将根据源码进行分析. 首先要说的是,HashMap中维护着的是一个数组: transient Node<K,V>[] table; ,数组中的每个元素都是一个 Node 对象.这里的Node是HashMap的一个内部类,代码如下: static class Node<K,V> implements Map.Entry<

单链表逆置

重写单链表逆置,熟能生巧- #include <iostream> #include <cstdlib> using namespace std; typedef struct List{ int num; struct List *next; }ListNode,*pListNode; void display(ListNode *pHead) { while(pHead) { cout<<pHead->num<<"--"; pH

java-------单链表

单链表: * 1.链表可以是一种有序或无序的列表 * 2.链表的内容通常存储在内存中分散的为止 * 3.链表由节点组成,每一个节点具有相同的结构 * 4.节点分为数据域和链域,数据域存放节点内容,链域存放下一个节点的指针 package myLinkList; public class MyLinkedList<T> { /** *Node:节点对象 * 包括数据域data和链域next(指向下一个节点对象) */ class Node { private T data; private No

数据结构之链表

---恢复内容开始--- 1:有头节点.单向.不循环链表 对于这种有头节点的单向不循环链表插入数据如下图: 1)头部插入 2)尾部插入 代码如下 : 1 #include "stdafx.h" 2 #include<iostream> 3 4 using namespace std; 5 6 typedef int DATA; 7 typedef struct node* LIST; 8 9 struct node 10 { 11 DATA data; 12 struct

拿java写了一个有点像指针的单链表

public class LinkList { private Node firstNode; private Integer position; public LinkList() {  super(); } public LinkList(Node firstNode) {  super();  this.firstNode = firstNode; } public Node getFirstNode() {  return firstNode; } public void setFirs

02 单链表

线性表之链式存储---单链表 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 // 数据结构 6 typedef struct node 7 { 8 int data; 9 struct node *next; 10 }linkList; 11 12 // 创建单链表,并初始化 13 linkList *linkList_init(void) 14 { 15 linkList *l

(单链表)单链表的整体逆序和局部逆序

题目一:将单链表翻转. 思路:有三种方式. 一:用数组存储单链表的值,然后重新逆序赋值,效率较低. 二:利用三个指针,在原来的基础上进行逆序.这种方法比较实用,效率也高. 三:从第2个节点到第N个节点,依次逐节点插入到第1个节点(head节点)之后,最后将第一个节点挪到新表的表尾.需要新建一个链表,这种方法和第二种差不多. 这里我就写出第二种方法,比较实用. 代码(方法二): struct ListNode { int val; ListNode *next; ListNode(int x) :

链表操作法则之逆向遍历与倒置算法

一.创建链表: 对链表进行操作的所有算法的前提,就是我们首先要创建一个链表,我们可以选择正向建链和逆向建链: (一).正向建链: 首先,我们得自定义节点类型: typedef struct Node { int data;//数据域 struct Node * pNext;//指针域 }NODE,*PNODE; 通过数组进行链表数据域的赋值: int main (void) { PNODE pHead;//头指针,接收创建链表时返回的头结点地址 int a[8] = {12,37,49,65,2