数据结构-线性表

复(预)习老师的ppt的屑。

顺序线性表部分: 

计算地址:

loc(ai) =loc(a1)+(i-1)×k 

 顺序表的基础要点:

1 无需为表示元素间的逻辑关系而增加额外的存储空间,存储密度大(100%);2 可随机存取表中的任一元素; 3 插入或删除一个元素时,需平均移动表的一半元素,具体的个数与该元素的位置有关,在等概率情况下,插入n/2,删除(n-1)/2;O(n) ;4 存储分配只能预先进行分配; 5 将两个各有n个元素的有序表归并为一个有序表,其最少的比较次数是:n

1:两个线性表LA和LB分别表示两个集合A和B,现求一个新的集合A=A∪B。

void union( List &La, List Lb)
{因为要修改A,传入的是A的指针
      La_len = ListLength(La);计算出两个长度
      Lb_len = ListLength(Lb);
      for( i=1; i<=Lb_len; i++){
            GetElem(Lb, i, e);得到B的第i位置的元素
            if(!LocateElem(La, e, equal))如果不在A内
                  ListInsert(La, ++La_len, e);加在A的后面,并且A的长度加一  
        }
}                       

时间复杂度为O(len(a) * len(b))。

2: 两个线性表LA和LB中的数据元素按值非递减有序排列,现将LA和LB归并为一个新的线性表,LC中的数据元素仍按值非递减有序排列。LA = (3, 5, 8, 11)  LB = (2, 6, 8, 9, 11, 15, 20) LC = (2, 3, 5, 6, 8, 8, 9, 11, 11, 15, 20)   

void MergeList( List La, List Lb, List &Lc)修改哪个哪个传指针
{    InitList(Lc);创建
      La_len = ListLength(La);  Lb_len = ListLength(Lb);
      i=j=1; k=0;
      while( (i<=La_len) && (j<=Lb_len)){
            a小是a,b小是b。
             GetElem(La, i, a); GetElem(Lb, j, b);
             if(a<=b) { ListInsert(Lc, ++k, a); ++i; }
             else { ListInsert(Lc, ++k, b); ++j; }
      }
      while(i<=La_len){
             GetElem(La, i++, a); ListInsert(Lc, ++k, a);}
      while(j<=Lb_len){ 
             GetElem(Lb, j++, b); ListInsert(Lc, ++k, b);}
}                       

 时间复杂度: O(ListLength(La)+ListLength(Lb)) 。

3:顺序表的基本操作

初始化 Status InitList_Sq ( SqList &L)
{
     L.elem = (ElemType * )malloc(LIST_INIT_SIZE*
                                                       sizeof(ElemType)); 
     if(!L.elem)
           exit(OVERFLOW);
     L.length = 0;
     L.listsize = LIST_INIT_SIZE;
     return OK;
}
L.elem = new ElemType[LIST_INIT_SIZE];
插入 Status ListInsert_Sq (SqList &L, int i, ElemType e)
{
      if( (i<1) || (i>L.length+1) )    
            return ERROR;
      if(L.length >= L.listsize){
            realloc(…); ….; // 越界处理 ;
      }
     q = &(L.elem[i-1]);
     for( p = &(L.elem[L.length-1]; p>=q; --p)
          *(p+1) = *p;
     *q = e;
     ++L.length;
     return OK;
}

插入时间复杂度分析:

 则长度为 n 的线性表中插入一个元素所需移动元素次数的期望值为:   E = ∑ pi (n – i + 1),如果等概率插入pi=1/(n+1),E=n/2,O(n)。 

删除 Status ListDelete_Sq (SqList &L, int i, ElemType &e)
{
     if( (i<1) || (i>L.length) )
          return ERROR;
     p = &(L.elem[i-1]);
     e = *p;
     q = L.elem+L.length-1;
     for( ++p; p<=q; ++p)
         *(p-1) = *p;
     --L.length;
     return OK;
}

 删除元素时间复杂度:

E=∑ qi (n – i),当等概率qi=1/n的时候,E=(n-1)/2,O(n)。

链式表部分:

用一组任意的存储单元存储线性表的元素,不要求逻辑上相邻的元素在物理位置上也相邻;

  

数据域用来存储结点的值;指针域用来存储数据元素的直接后继的地址(或位置)。 

※基本概念

1结点:数据元素的存储映象。

2头指针: 指示链表中第一个结点的存储位置,单链表可由头指针唯一确定。设置头结点的目的是统一空表与非空表的操作,简化链表操作的实现。

3首元结点:链表中存储线性表中第一个数据元素的结点。

【头指针,头结点、首元节点】_xinda的博客-CSDN博客_首元结点链表中第一个结点的存储位置叫做头指针,那么整个链表的存取就必须是从头指针开始进行了。之后的每一个结点,其实就是上一个的后继指针指向的位置。这里有个地方要注意,就是对头指针概念的理解,这个很重要。“链表中第一个结点的存储位置叫做头指针”,如果链表有头结点,那么头指针就是指向头结点数据域的指针。画一个图吧。 头指针就是链表的名字。头指针仅仅是个指针而已。头结https://blog.csdn.net/liangxingda/article/details/52755800?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164585205316781683917399%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164585205316781683917399&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-2-52755800.pc_search_result_control_group&utm_term=%E9%A6%96%E5%85%83%E8%8A%82%E7%82%B9%EF%BC%8C%E5%A4%B4%E8%8A%82%E7%82%B9&spm=1018.2226.3001.4187

 基本操作:

初始化线性表InitList(L)
      该运算建立一个空的单链表,即创建一个头结点。
      void InitList(LinkList &L)
      {
	L=(LinkList)malloc(sizeof(LNode));  	
               /*创建头结点*/
	L->next=NULL;
      }
销毁线性表DestroyList(L)
      释放单链表L占用的内存空间。即逐一释放全部结点的空间。
    void DestroyList(LinkList L)
    {	LinkList p=L, q=p->next;
	while (q!=NULL)
	{      free(p);
	        p=q;q=p->next;
	}
	free(p);
    }
插入
Status ListInsert(LinkList &L, int i, ElemType e)
{
     p = L; j = 0;
     while (p && j < i-1) { p = p->next; ++j }
     if (!p || j>i-1)  return ERROR;
     s =  (LinkList) malloc( sizeof (LNode) );
     s->data = e;  
     s->next = p->next;  ①
     p->next = s;  ②
     return OK;
}
删除
Status ListDelete(LinkList &L, int i, ElemType &e)
{
     p = L; j = 0;
     while (p->next && j < i-1) { p = p->next; ++j }
     if (!(p->next) || j>i-1)  return ERROR;
     r = p->next;
     e = r->data;  
     p->next = p->next->next;  //(p->next = r->next;) ①
     free(r);  
     return OK;
}
删除*p结点的语句序列
   q = L;
   while( q->next != p)   q = q->next;
   q->next = p->next;
   free(p);
删除首元结点的语句序列
   q = L->next;
   L->next = q->next;
   free(q);
删除最后一个结点的语句序列
    while(p->next->next != NULL)   p = p->next;
    q = p->next;
    p->next = NULL;
    free(q);

建表创建单链表的头插法与尾插法详解_Frank的博客-CSDN博客_头插法和尾插法创建单链表关于数据结构的入门,就是从顺序表和单链表开始。我们不讲顺序表,直接从单链表开始我们的数据结构和算法的学习之路。单链表就是一种特殊的结构体组合而成的数据结构,关于单链表的创建方法有很多种,但都大同小异。正如这幅图中所表示的那样,单链表就是由可能不连续的数据所组合而成的数据结构。 其中每个数据分为两部分,一部分是数据存储的位置,称为数据域,另外指针所存储的地方,称为指针域。typ...https://blog.csdn.net/qq_41028985/article/details/82859199?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164585271716780271568338%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=164585271716780271568338&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-82859199.pc_search_result_control_group&utm_term=%E5%A4%B4%E6%8F%92%E6%B3%95%E5%92%8C%E5%B0%BE%E6%8F%92%E6%B3%95&spm=1018.2226.3001.4187

头插法建表
CreateList_H(LinkList &L, int n)
{
     L = (LinkList) malloc( sizeof (LNode) );
     L->next = NULL;
     for( i=n; i>0; --i){
         s = (LinkList) malloc( sizeof (LNode) );
         scanf( &s->data);
         s->next = L->next; ①
         L->next = s; ②
     }
}
尾插法建表
就是引入了一个tail始终指向链表的末尾,使得插入变的简单。
CreateList_T(LinkList &L, int n)
{
     tail = L = (LinkList) malloc( sizeof (LNode) );
     L->next = NULL;
     for( i=n; i>0; --i){
         s = (LinkList) malloc( sizeof (LNode) );
         scanf( &s->data);
         s->next = NULL; 
         tail->next = s; 
         tail = s; 
     }
}

变式:

1 循环链表

循环链表是另一种形式的链式存储结构, 可从当前结点出发,访问到任一结点。有循环单链表、 多重循环链表。

操作与线性单链表基本一致,差别只是在于算法中的循环结束条件不是p是否为空,而是p是否等于头指针

2  双向链表

 typedef struct DuLNode{    

         ElemType               data;      

        struct DuLNode    *prior;      struct DuLNode    *next;

}DuLNode,  *DuLinkList;

 3 双向循环链表

静态链表:

有些高级程序设计语言并没有指针类型,如FORTRAN和JAVA。我们可以用数组来表示和实现一个链表,称为静态链表。 可定义如下:

#define  MAXSIZE  1000   //最多元素个数

typedef  struct{        

        ElemType data;        int              cur; //游标,指示器

}component, SLinkList[MAXSIZE];

练习题:

约瑟夫环

习题集P79。编号为1,2,...,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。现在给定一个随机数m>0,从编号为1的人开始,按顺时针方向1开始顺序报数,报到m时停止。报m的人出圈,同时留下他的密码作为新的m值,从他在顺时针方向上的下一个人开始,重新从1开始报数,如此下去,直至所有的人全部出圈为止。

问题输入 :输入数据第一行为两个正整数n和m,分别表示人的个数及初始随机数,每组数据的第二行为n个整数,表示每个人持有的密码。

问题输出 :用一行输出n个整数表示依次出圈人的编号,整数之间用空格分隔   

方法一:尾插法建立链表,循环判断。终止条件是循环链表只有一个元素 

#include<stdio.h>
#include<stdlib.h>
typedef struct link
{
	int data;
	int id;
	struct link* next;
}List;
List* Creatlist() {
	List* L = (List*)malloc(sizeof(List));
	L->next = NULL;
	return L;
}
void Insert(List* p, int d,int i)
{//插入新的节点并且返回尾节点
	//printf("INSERT>>>\n");
	List* L = (List*)malloc(sizeof(List));
	L->next = NULL;
	L->data = d; L->id = i;
	p->next = L;
	return ;
}
int main()
{	//初始化过程
	int num, m,i,d;	scanf_s("%d%d", &num, &m);
	//创建n个节点,写循环指针
	struct link* head =Creatlist();
	scanf("%d", &d);
	head->data = d; head->id=1;
	List* p = head; List* Q = (List*)malloc(sizeof(List));
	for (i = 2; i <= num; i++)
	{
		//printf("INSERT>>");
		scanf("%d", &d);
		Insert(p,d,i);
		p = p->next;
	}
	if (num == 1)
	{
		printf("1 ");
		return 0;
	}
	p->next = head;//循环
	//执行判断
	while (1)
	{
		for (i = 1; i < m; i++)
		{
			p = head;
			head = head->next;
		}
		m = head->data;
		printf("%d ", head->id);
		p->next = head->next;
		Q = head;	head = head->next;
		free(Q);
		if (head->next == head->next->next)
			break;
	}
	printf("%d", head->id);
	free(head); 
	return 0;
 
}

方法二:整体上优化了。 使用两个for循环控制循环次数,没有引入乱七八糟的函数。

#include<stdio.h>
#include<stdlib.h>
typedef struct list{
	int data;
	int id;
	struct list *next;
}L;
//注意有两个数据,一个是编号,一个是分配的数值 
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	//1 建表
	L*list=(L*)malloc(sizeof(L));
	L*tail=list,*head=list,*p=list;
	head->id=1;head->next=NULL;
	//第一个需要单独 
	scanf("%d",&head->data);
	for(int i=2;i<=n;i++)
	{
		L*node=(L*)malloc(sizeof(L));
		node->next=NULL;
		node->id=i;
		scanf("%d",&node->data);
		tail->next=node;
		tail=node;
	}
	tail->next=head;
	if(m==1)
	{
		printf("1");
		return 1;
	 } 
	//2 循环判断 	 
	for(int i=1;i<=n;i++)
	{
		 
		for(int j=1;j<m;j++)//锁定前一个 
		{//同时使得p和head都 循环 
			p=head;//上一个 
			head=head->next;//当前 
		}
		m=head->data;
		printf("%d ",head->id);
		p->next=head->next;	
		tail=head;
		head=head->next;
		free(tail);
	}
	return 1;
 } 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值