【408数据结构】线性表(1):链表

线性表(1):链表

本专栏的记录,只适用于408的考试。其它考试请酌情参考。
本章应用题和算法题⛳都可能考,尤其是算法题。
ps: 由于408不太要求程序能编译或者是健壮性良好,因此文中代码可能会不能跑起来。

线性表

  1. 定义

    零个或多个数据元素的有限序列。【允许空表】

    有限性、序列。

  2. 直接前驱、直接后继

    1. 谁有:0-n-1——有直接后继
  3. 线性表的基本操作【考试直接用的那种】

    1. InitList (*L)
    2. ListEmpty (L)
    3. ClearList (*L)
    4. GetElem (L, i, *e)
    5. LocateElem(L, e)
    6. ListInsert (*L, i, e)
    7. ListDelete (*L, i, e)
    8. ListLength (L)

链式存储

  1. 定义

    结点定义如下:

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

    除了数据以外,还有一个存放其后继的指针。离散存储,【失去了随机存取】

  2. 基本操作

    1. 建立单链表

      1. 头插法:【插入时间为 O ( 1 ) O(1) O(1),但是,与原始插入顺序相反】

        【带有头节点版】

        LinkList List_head_Insert(LinkList &L)
        {
        	LNode *s;
        	int x;
        	L = (LinkList) malloc(sizeof(LNode));
        	L -> next = NULL;
        	scanf("%d",&x);
        	while(x!=-1)
        	{
        		s = (LNode *)malloc (sizeof(LNode));
        		s -> data = x;
        		s -> next = L -> next;
        		L -> next = s;
        		scanf("%d",&x);
        	}
        	return L;
        }
        

        【不带有头节点版】

        LinkList List_head_Insert(LinkList &L_head)
        {
        	LNode *s;
        	int x;
        	L_head = (LinkList) malloc(sizeof(LNode));
        	L_head -> next = NULL;
        	scanf("%d",&x);
        	if (x == -1) // end of the link
        	{
        		delete(L_head);
        		return NULL;
        	}
        	L_head -> data = x;
        	scanf("%d",&x);
        	while(x!= -1)
        	{
        		s = (LNode *)malloc (sizeof(LNode));
        		s -> data = x;
        		s -> next = L_head;
        		L_head = s;
        		scanf("%d",&x);
        	}
        	return L_head;
        }
        
        
      2. 尾插法

        【带有头节点版】

        LinkList List_head_Insert(LinkList &L)
        {
        	LNode *s, *rear;
        	int x;
        	L = (LinkList) malloc(sizeof(LNode)); // 建立头节点
        	L -> next = NULL;
        	rear = L;
        	scanf("%d",&x);
        	while(x!=-1)
        	{
        		s = (LNode *)malloc (sizeof(LNode));
        		s -> data = x;
        		s -> next = NULL;
        		rear -> next = s;
        		rear = s;
        		scanf("%d",&x);
        	}
        	return L;
        }
        

        【不带有头节点版】

        LinkList List_head_Insert(LinkList &L_head)
        {
        	LNode *s, *rear;
        	int x;
        	L_head = (LinkList) malloc(sizeof(LNode));
        	L_head -> next = NULL;
        	scanf("%d",&x);
        	if (x == -1) // end of the link
        	{
        		delete(L_head);
        		return NULL;
        	}
        	L_head -> data = x;
        	rear = L_head;
        	scanf("%d",&x);
        	while(x!= -1)
        	{
        		s = (LNode *)malloc (sizeof(LNode));
        		s -> data = x;
        		s -> next = NULL;
        		rear -> next = s;
        		rear = s;
        		scanf("%d",&x);
        	}
        	return L_head;
        }
        
    2. 查找

      1. 按值查找 :just a for-loop【 O ( N ) O(N) O(N)
      2. 按序号查找:失去了随机存取的特性【 O ( N ) O(N) O(N)
    3. 插入

      1. 后插

        int List_head_Insert
        (LinkList &L_head, LNode *insert_point, LNode *location)// L_head是单链表,insert_point为要插入的结点,而location是插入的位置的指针
        {
        	// 为什么没必要呢?因为已经给定了location结点了。可以直接用它来了做后插了。
        	~~LNode *p = L_head -> next;
        	LNode *mid;
        	while(p -> next)
        	{
        		if (p == location)
        		{~~
        			mid = p -> next;
        			p -> next = insert_point;
        			insert_point -> next = mid;
        			return 1;
        			
        			// 可以不要mid
        			insert_point -> next = p -> next;
        			p -> next = insertion;
        		~~}
        		p = p -> next;
        	}
        	return 0;
        }~~
        
      2. 前插

        【一种比较笨的前插】: O ( N ) O(N) O(N)

        int List_head_Insert
        (LinkList &L_head, LNode *insert_point, LNode *location)// L_head是单链表,insert_point为要插入的结点,而location是插入的位置的指针
        {
        	LNode *p = L_head -> next;
        	LNode *fore_point = L_head ;
        	LNode *mid;
        	while(p -> next)
        	{
        		
        		if (p == location)
        		{
        			mid = fore_point -> next;
        			fore_point  -> next = insert_point;
        			insert_point -> next = mid;
        			return 1;
        		}
        		fore_point = p;
        		p = p -> next;
        	}
        	return 0;
        }
        

        【一种基于交换结点data的后插】: O ( 1 ) O(1) O(1)

        int List_head_Insert
        (LinkList &L_head, LNode *insert_point, LNode *location)// L_head是单链表,insert_point为要插入的结点,而location是插入的位置的指针
        {
        	LNode *mid = location -> next;
        	location -> next = insert_point;
        	insert_point -> next = mid;
        	
        	// 可以不要mid
        	insert_point -> next = p -> next;
        	p -> next = insertion;
        	swap(location -> data, insert_point -> data);
        	return 1;
        }
        
    4. 删除

      O ( N ) O(N) O(N)

几种改进链表

408考试中还没有出现以下几种列表。请酌情复习。

双链表

前驱+后继

typedef struct DNode
{
	ElemType data;
	struct DNode *prior, *next;
}DNode, *DLinkList;

循环链表

最后一个结点的指针指向头结点。

静态链表

利用数组的存放,进行模拟。

补充:一元多项式的计算和存储

单链表算法题常用套路

原地逆置算法

【利用头插法的思想】

空间复杂度为 O ( 1 ) O(1) O(1),时间复杂度为 O ( N ) O(N) O(N)

void reverse_in_O_1(LNode &L)
{
	LNode *p, *fast;
	p = L.next;
	while (p != NULL)
	{
		fast = p -> next;
		p -> next = L.next;
		L.next = p;
		p = tmp; 
	}

}

单链表双指针处理

【用一次遍历,找到链表中间结点n/2的那个点。】

空间复杂度 O ( 1 ) O(1) O(1),时间复杂度 O ( N ) O(N) O(N)

int find_mid(LinkList &L)
{
	LNode *p, *p2;
	p = L.next;
	p2 = L.next;
	while(p2 == NULL || p2->next == NULL)
	{
		p2 = p2 -> next -> next;
		p = p -> next;
	}
	return p -> data;
}

【用一次遍历,找到链表的倒数第k个元素】

空间复杂度 O ( 1 ) O(1) O(1),时间复杂度 O ( N ) O(N) O(N)

int find_k(LinkList &L, int k)
{
	LNode *p = L.next, *pk = L.next;
	while (k--)
	{
		pk = pk -> next;
	}
	if (pk == NULL)
		return -1; //链表长度小于k
	while(pk)
	{
		pk = pk -> next;
		p = p -> next;
	}
	return p -> data;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值