学习路上的链表

本文探讨了链表作为数据结构的优势,对比了数组的局限性,并通过实例展示了如何使用链表在有序序列中插入元素并保持有序。同时,详细解析了用链表解决约瑟夫环问题的过程,包括创建、插入、删除等基本操作,并给出了不同实现方式的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 为什么要链表

因为数组有至少两个局限:

(1)编译期就要知道大小

(2)数组中的数据在计算机内存中是以相同的距离间隔开的,这意味在数组中插入一个数据,需要挪动数组中其他数据。

链表是好东西,实现链表最灵活的方法是指针。

以给出一组有序的序列后向序列中加入新的元素,且保证序列仍然有序为例

用数组模拟链表:

原理如图:

开俩个数组,一个用来存数一个用来存该数右边数的序号;

下面是刚学的时候对着图写的代码:

#include<bits/stdc++.h>
using namespace std;
int a[100001];
int b[100001];
int main()
{
	int n;
	cin>>n;
	b[0]=1;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		b[i]=i+1;
	
	}
	
	b[n]=0;//防止陷入死循环; 
	cin>>a[n+1];
	int t;
	for(t=0;;t=b[t])
	{
		if(a[b[t]]>a[n+1])
		{
			b[n+1]=b[t];
			b[t]=n+1;
			break;
		}
	}
	int ti=1;
	for(t=0;;t=b[t])
	{
		if(ti==n+2)break;
		cout<<a[b[t]]<<" ";
		ti++;
	
	 } cout<<endl;
	return 0;
}

书上的代码如下:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int a[101],id[101];
	int i,n,t,len;
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	len=n;
	len++;
	for(int i=1;i<=n;i++)
	{
		if(i!=n)
		id[i]=i+1;
		else 
		id[i]=0;
	 } 
	 cin>>a[len];
	 t=1;
	 while(t)
	 {
	 	if(a[t]>a[len])
	 	id[len]=id[t];
	 	id[t]=len;
	 	break;
	 }
	 t=1;
	 while(t)
	 {
	 	cout<<a[t]<<" ";
	 	t=id[t];
	 	
	 }
	return 0;
}

但书上这样写有bug,就是当给出的元素比原序列第一个元素小的时候,不能正确排序;

改进后代码:

#include<bits/stdc++.h>
using namespace std;
int num[100010];
int nxt[100010];
int idx = 0, head = -1;

void odinsert(int x)//初始插入元素 
{
    nxt[idx] = head;
    head = idx;
    num[idx] = x;
    idx++;
}

void del(int k)//删除k的一个后的元素; 
{
    nxt[k] = nxt[nxt[k]];
}

void nxtwinsert(int k, int x)//将x插入k位置; 
{
    num[idx] = x;
    nxt[idx] = nxt[k];
    nxt[k] = idx;
    idx++;
}

int main()
{
   int n;
   cin>>n;
   int a;
   for(int i=0;i<n;i++)
   {
    	cin>>a;
	   odinsert(a); 
   }
   cin>>num[n]; 
   for(int i=0;i<n;i++)
   {
   	if(num[i]>num[n])
   	{
   		nxtwinsert(i,num[n]);
   		break;
	   }
   }
    while(nxt[head] != -1)//遍历出来是倒叙的; 
	{
        cout << num[head] << " ";
        head = nxt[head];
    }
    cout<<num[head];
    cout << endl;
    return 0;
}

结构体指针链表:

#include<stdio.h>
#include<stdlib.h>
struct node
{
	int num;
	struct node *next;
};
int main()
{
	int n;
	scanf("%d",&n);
	struct node *op,*np,*head=NULL,*flag;
	int a;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a);
		np=(struct node*)malloc(sizeof(struct node));
		np->next=NULL;
		np->num=a;
		if(head==NULL)
		 {
		 	flag=np;
		 	head=np;
		 }
		else 
		 op->next=np;
		
		op=np;  
    }
    scanf("%d",&a);

    while(head!=NULL)
    {
    	if(head->next->num  >a)
    	{
    		np=(struct node *)malloc(sizeof(struct node));
    		np->num=a;
			np->next=head->next;
    		head->next=np;
    		
    		break;
		}
		head=head->next ;
	}
	
	while(flag!=NULL)
	{
		printf("%d ",flag->num);
		flag=flag->next;
	}
	return 0;
}

链表的各种操作

创建节点:

typedef struct node
{
	int num;
	struct node;
	
}node;//声明node节点这个新类型 

创建链表:

node* creat(int n)
{
	node *head, *op,*np;
	head = (node *)malloc(sizeof(node));
	op = head; //头节点为0 加上头节点共n+1个节点
	head->num = 0;
	head->next = NULL;
	int a;
	 
	for (int i = 1; i <= n; i++)
	{
		scanf("%d",&a);
		node *np = (node *)malloc(sizeof(node));
		np->num = a;
		np->next = NULL;
		op->next = np;
		op = op->next;
	}
	return head;
}
 

node *creat(int n)
{
	node *op,*np,*head=NULL;
	int a;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a);
		np=(struct node*)malloc(sizeof(struct node));
		np->next=NULL;
		np->num=a;
		if(head==NULL)
		 
		 	head=np;
		else 
		 op->next=np;
		
		op=np;  
	}
	return head;
 } 

输出链表:

void print(node *head)
{
	node *pp=head->next ;//为了不改变head?
	while(pp!=NULL)
	{
		printf("%d ",pp->num );
		pp=pp->next;
	}
}

删除节点:

void delet(int k,node *head)//删除值为k的节点
{
   node *pre,*current;
   pre=head;
   current=head->next;
   while(current!=NULL)
   {
    if(current->num==k)
     {
      pre->next=current->next;
      free(current);
      break;
      
     } 
     pre=current;
     current =current->next;
    }
   
}

插入节点:

void insert(int k, node *head)//向链表头部插入节点 
{
	//创建新节点
	node *np = (node *)malloc(sizeof(node));
	np->num = k;
	
	node* cun = head->next;

	np->next = cun;
	head->next = np;
}

应用:

n个人围成一圈,从第1个人开始顺序报号1,2,3。凡报到3者退出圈子。找出最后留在圈子中的人原来的序号。要求用链表实现。

首先看看非链表做法:即约瑟夫环 (先鸽着寒假再写)

#include<stdio.h>
int main()
{
    int n;
    while (scanf("%d", &n) != EOF)
    {
        int s = 0;
        for (int i = 2; i <= n; i++)
        {
            s = (s + 3) % i;
        }
        printf("%d\n", s + 1);
    }
    return 0;
}

链表做法:

#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
	int num;
	struct node *next;
}node;

node *creat(int n)
{
	node *head,*np,*op;
	head=(node*)malloc(sizeof(node));
	head->num =0;
	head->next =NULL;
	op=head;
	for(int i=1;i<=n;i++)
	{
		np=(node*)malloc(sizeof(node));
		np->num =i;
		np->next =NULL;
		op->next =np;
		op=np;
	}
	np->next =head->next   ;
	return head;
}


int main()
{
	int n;
	scanf("%d",&n);
    node *head=creat(n);
    int i=0;
    node *pre,*cur;
    cur=head->next ;
    pre=NULL;
    int flag=-1;
    while(1)
    {
    	i++;
    	if(flag==cur->num)
    	break;
    	flag=cur->num ;
    	if(i==3)
    	{
    		pre->next =cur->next ;
    		free(cur);
    		i=0;
		}
		pre=cur;
		cur=cur->next; 
	}
	printf("%d",flag);
	return 0;
}

 具体到每一步:

#include <stdio.h>

struct people
{
	int id;//记录报的数 
	struct people *next;
};

int main()
{
	int n;
	scanf("%d",&n);
	
	int count = n;
	people p[n];
	people *head;
	head = p; //head 指向p[0]
	
	for (int i = 0; i < n; i++)
	{
		head->id = i + 1;
		head->next = &p[i + 1];
		head = head->next;
	}
    //最后一个元素指向第一个元素 ,从而 形成环
	p[n - 1].next = p; 

	int i = 1;
	head = p;
	while (count > 1)
	{
        //跳过已经被淘汰的节点
		if (head->id == 0)
		{
			head = head->next;
			continue;
		}
		if (i == 3)
		{
            //被淘汰的节点,n置为0
			printf("第 %d 位置被淘汰\n", head->id);
			head->id = 0;
			count--;
		}
		head = head->next;
		i++;
		if (i > 3)
		{
			i = 1;
		}
	}
	printf("--------------\n");
	while (head->id == 0)
	{
        //非0节点即为最后留下的
		head = head->next;
		if (head->id != 0)
		{
			printf("留到最后的是 %d \n", head->id);
		}
	}

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值