数据结构之单链表及其函数算法

---恢复内容开始---

  单链表是非常重要的数据结构,动态在数据处理中确实是及其必要的,静态的比如定义一个数组,大小是固定的,定小了不够用,定多了浪费空间,链表增删数据不需要像数组那样要移动数据,但是也付出了需要提供指针域的空间。
  单链表分为带头指针和不带头指针,头指针意思是它里面没数据元素,仅仅有指向下一个结点的指针,也就是说第二个结点才是存放的第一个数据元素,为了防止删除或者增加数据元素在第一个元素的位置而带来的更改头指针的麻烦。
  下面是完整的代码,附带解释来研究单链表及其功能函数。
//快慢指针的问题,比如找一个单链表的中间节点,可以i每次走一个,j每次走两个。
#define _CRT_SECURE_NO_DEPRECATE//vs编译器c语言需要加此条代码
#include<stdio.h>
#include<stdlib.h>
#define DataType int   //定义数据类型
typedef struct LNode   //结点数据类型
{
	DataType data;
	struct LNode *next;
}LNode,*Linklist;     //结点非指针类型和指针类型定义
//接下来是两种构造单链表的算法尾插法和头插法
void CreatListTail(Linklist &L, int n)   //尾插法构造长度为n的单链表
{
	L = (Linklist)malloc(sizeof(LNode));//动态申请一个头结点空间
	L->next = NULL;  //初始化头为空
	//注意在C语言中结构变量若是指针类型Linklist则要用->,若不是指针变量LNode则用.(L.next)
	printf("Input %d datas:\n",n);
	Linklist s,p=L;  //定义指针变量s来申请空间,指针变量p指向链表尾
	DataType x;     //定义变量来接收输入的数据元素
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &x);
		s = (Linklist)malloc(sizeof(LNode));
		s->data = x;
		s->next = p->next;   //尾插法,在p之后插入s所指结点
		p->next = s;
		p = s;   //p指向最后
	}
}
void CreatListHead(Linklist &L, int n)//头插法构造长度为n的单链表
{
	L = (Linklist)malloc(sizeof(LNode));
	L->next = NULL;
	printf("Input %d datas:\n", n);
	Linklist s;
	DataType x;
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &x);
		s = (Linklist)malloc(sizeof(LNode));
		s->data = x;
		s->next=L->next;   //头插法
		L->next = s;
	}
}
void PrintList(Linklist L)//遍历输出
{
	Linklist p = L->next;
	while (p)
	{
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}
int find(Linklist L, DataType x)//查找某一元素位置,没有则返回0
{
	Linklist p = L->next;
	int j = 1;
	while (p)   //如果未遍历完则循环找
	{
		if (p->data == x)
			return j;
		j++;
    p=p->next; } return 0; } Linklist Get(Linklist L, int i) //返回第i个元素的地址 { Linklist p = L->next; int j = 1; while (p&&j < i) //链表未遍历完且还没到第i个元素 p = p->next; if (p&&j == i) return p; else return 0; } bool ListInsert(Linklist &L, DataType x,int i) //第i个位置插入元素x { Linklist p = L; int j = 0; while (p&&j < i - 1)//找第i-1个元素地址 { p = p->next; j++; } if (p&&j == i - 1)//第i-1个元素后面插入新结点 { Linklist s = (Linklist)malloc(sizeof(LNode)); s->data = x; s->next = p->next; p->next = s; return true; } else return false; } bool ListDelete(Linklist &L, int i)//删除第i个元素,与上面类似先找第i-1个元素位置 { Linklist p = L; int j = 0; while (p&&j < i - 1) { p = p->next; j++; } if (p&&j == i - 1) { Linklist s = p->next; p->next = s->next; free(s); return true; } else return false; } void ListReverse(Linklist &L)//头插法就地转置 { Linklist p = L->next,s;//p为元素头 L->next = NULL;//L为新的头结点 while (p) { s = p; p = p->next; s->next = L->next; L->next = s; } } void ListInsertSort(Linklist &L)//插入法排序 { Linklist head=L->next; L->next = NULL; Linklist p,s; while (head) { s = head; head = head->next; p = L; while (p->next&&p->next->data <= s->data) p = p->next; s->next = p->next; p->next = s; } } void ListSlectSort(Linklist &L)//选择排序 { Linklist p=L, s,q,min; while (p->next) { q = p; s = p; while (s->next) { if (s->next->data < q->next->data)//q指向最小值的前驱 q = s; s = s->next; } min = q->next; if (p != q) { q->next = min->next;//记下min min->next = p->next; p->next = min; } p = p->next;//已经确保最后NULL结尾 } } int main()//测试代码 { Linklist L; CreatListHead(L,6); CreatListTail(L, 6); PrintList(L); ListReverse(L); PrintList(L); /*ListInsertionSort(L);*/ ListSlectSort(L); PrintList(L); //if (ListInsert(L, 32, 4)) // printf("do insert!\n"); //else // printf("fail insert!\n"); //PrintList(L); //if(ListDelete(L, 3)) // printf("do delete !\n"); //else // printf("fail delete!\n"); //PrintList(L); //int x = 32; //if (find) // printf("%d is in %d", x,find(L, x)); //else // printf("fail find %d!\n",x); //PrintList(L); system("pause"); return 0; }
  
  快慢指针问题:单链表在面试题中会有一种比较难的算法题——快慢指针。例如如何找到一个单链表的最中间的节点,如何判断一个单链表是否有环,面试题一定要最优解法。具体算法是定义两个快慢指针,第一个指针每次往下走一个结点,第二个指针每次往下走两个结点,两个指针同步移动,针对第一个题当快指针走到链表末尾时慢指针的位置就是链表中心点,第二道题类比于跑步套圈,当慢指针和快指针相遇则一定有环。
  链表交点问题:找到两个单链表交点的位置,分析单链表有唯一后继,那么如果有交点,则交点之后两个链表必重合。最有效的算法是分别算两个单链表的长度X,长度差就是两个单链表从开始到交点的长度差,长的链表遍历X长度,然后两者再同步遍历,找到相同结点。

---恢复内容结束---

  单链表是非常重要的数据结构,动态在数据处理中确实是及其必要的,静态的比如定义一个数组,大小是固定的,定小了不够用,定多了浪费空间,链表增删数据不需要像数组那样要移动数据,但是也付出了需要提供指针域的空间。
  单链表分为带头指针和不带头指针,头指针意思是它里面没数据元素,仅仅有指向下一个结点的指针,也就是说第二个结点才是存放的第一个数据元素,为了防止删除或者增加数据元素在第一个元素的位置而带来的更改头指针的麻烦。
  下面是完整的代码,附带解释来研究单链表及其功能函数。
//快慢指针的问题,比如找一个单链表的中间节点,可以i每次走一个,j每次走两个。
#define _CRT_SECURE_NO_DEPRECATE//vs编译器c语言需要加此条代码
#include<stdio.h>
#include<stdlib.h>
#define DataType int   //定义数据类型
typedef struct LNode   //结点数据类型
{
	DataType data;
	struct LNode *next;
}LNode,*Linklist;     //结点非指针类型和指针类型定义
//接下来是两种构造单链表的算法尾插法和头插法
void CreatListTail(Linklist &L, int n)   //尾插法构造长度为n的单链表
{
	L = (Linklist)malloc(sizeof(LNode));//动态申请一个头结点空间
	L->next = NULL;  //初始化头为空
	//注意在C语言中结构变量若是指针类型Linklist则要用->,若不是指针变量LNode则用.(L.next)
	printf("Input %d datas:\n",n);
	Linklist s,p=L;  //定义指针变量s来申请空间,指针变量p指向链表尾
	DataType x;     //定义变量来接收输入的数据元素
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &x);
		s = (Linklist)malloc(sizeof(LNode));
		s->data = x;
		s->next = p->next;   //尾插法,在p之后插入s所指结点
		p->next = s;
		p = s;   //p指向最后
	}
}
void CreatListHead(Linklist &L, int n)//头插法构造长度为n的单链表
{
	L = (Linklist)malloc(sizeof(LNode));
	L->next = NULL;
	printf("Input %d datas:\n", n);
	Linklist s;
	DataType x;
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &x);
		s = (Linklist)malloc(sizeof(LNode));
		s->data = x;
		s->next=L->next;   //头插法
		L->next = s;
	}
}
void PrintList(Linklist L)//遍历输出
{
	Linklist p = L->next;
	while (p)
	{
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}
int find(Linklist L, DataType x)//查找某一元素位置,没有则返回0
{
	Linklist p = L->next;
	int j = 1;
	while (p)   //如果未遍历完则循环找
	{
		if (p->data == x)
			return j;
		j++;
    p=p->next; } return 0; } Linklist Get(Linklist L, int i) //返回第i个元素的地址 { Linklist p = L->next; int j = 1; while (p&&j < i) //链表未遍历完且还没到第i个元素 p = p->next; if (p&&j == i) return p; else return 0; } bool ListInsert(Linklist &L, DataType x,int i) //第i个位置插入元素x { Linklist p = L; int j = 0; while (p&&j < i - 1)//找第i-1个元素地址 { p = p->next; j++; } if (p&&j == i - 1)//第i-1个元素后面插入新结点 { Linklist s = (Linklist)malloc(sizeof(LNode)); s->data = x; s->next = p->next; p->next = s; return true; } else return false; } bool ListDelete(Linklist &L, int i)//删除第i个元素,与上面类似先找第i-1个元素位置 { Linklist p = L; int j = 0; while (p&&j < i - 1) { p = p->next; j++; } if (p&&j == i - 1) { Linklist s = p->next; p->next = s->next; free(s); return true; } else return false; } void ListReverse(Linklist &L)//头插法就地转置 { Linklist p = L->next,s;//p为元素头 L->next = NULL;//L为新的头结点 while (p) { s = p; p = p->next; s->next = L->next; L->next = s; } } void ListInsertSort(Linklist &L)//插入法排序 { Linklist head=L->next; L->next = NULL; Linklist p,s; while (head) { s = head; head = head->next; p = L; while (p->next&&p->next->data <= s->data) p = p->next; s->next = p->next; p->next = s; } } void ListSlectSort(Linklist &L)//选择排序 { Linklist p=L, s,q,min; while (p->next) { q = p; s = p; while (s->next) { if (s->next->data < q->next->data)//q指向最小值的前驱 q = s; s = s->next; } min = q->next; if (p != q) { q->next = min->next;//记下min min->next = p->next; p->next = min; } p = p->next;//已经确保最后NULL结尾 } } int main()//测试代码 { Linklist L; CreatListHead(L,6); CreatListTail(L, 6); PrintList(L); ListReverse(L); PrintList(L); /*ListInsertionSort(L);*/ ListSlectSort(L); PrintList(L); //if (ListInsert(L, 32, 4)) // printf("do insert!\n"); //else // printf("fail insert!\n"); //PrintList(L); //if(ListDelete(L, 3)) // printf("do delete !\n"); //else // printf("fail delete!\n"); //PrintList(L); //int x = 32; //if (find) // printf("%d is in %d", x,find(L, x)); //else // printf("fail find %d!\n",x); //PrintList(L); system("pause"); return 0; }
  
  快慢指针问题:单链表在面试题中会有一种比较难的算法题——快慢指针。例如如何找到一个单链表的最中间的节点,如何判断一个单链表是否有环,面试题一定要最优解法。具体算法是定义两个快慢指针,第一个指针每次往下走一个结点,第二个指针每次往下走两个结点,两个指针同步移动,针对第一个题当快指针走到链表末尾时慢指针的位置就是链表中心点,第二道题类比于跑步套圈,当慢指针和快指针相遇则一定有环。
  链表交点问题:找到两个单链表交点的位置,分析单链表有唯一后继,那么如果有交点,则交点之后两个链表必重合。最有效的算法是分别算两个单链表的长度X,长度差就是两个单链表从开始到交点的长度差,长的链表遍历X长度,然后两者再同步遍历,找到相同结点。
#include<stdio.h>
#include<stdlib.h>
typedef int DataType;
typedef struct LNode
{
    DataType data;
    struct LNode *next;
}LNode, *Linklist;
//带头结点的单链表判断是否带环,有环的话返回入环节点的指针,否则返回NULL
Linklist FindCircleNode(Linklist L)
{
    if (!L)
        return NULL;
    Linklist p = L;
    Linklist q = L;
    while (p)
    {
        q = q->next;
        if (p->next)
        {
            p = p->next->next;
        }
        else
            return NULL;
        if (p == q)
            break;
    }
    if (!p)
        return NULL;
    p = L;
    while (q != p)
    {
        p = p->next;
        q = q->next;
    }
    return p;
}
int main()
{
    Linklist link[10];
    for (int i = 0; i < 30; i++)
    {
        link[i] = (Linklist)malloc(sizeof(LNode));
        link[i]->data = i;
    }
    for (int i = 0; i < 29; i++)
    {
        link[i]->next = link[i + 1];
    }
    link[29]->next = link[12];
    Linklist res = FindCircleNode(link[0]);
    if (!res)
        printf("该链表没有环!\n");
    else
        printf("该链表有环,入环节点值为:%d\n", res->data);
    getchar();
    return 0;
}

 

转载于:https://www.cnblogs.com/BetterThanEver_Victor/p/5145298.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值