数据结构 线性表的应用——经典题目分析

目录

1、逆置单链表

2、反转单链表

3、合并两个有序的单链表

4、判断单链表是否有环?环的入口点?环的长度?

5、判断两个单链表是否相交?交点?

6、O(1)时间删除单链表的一个节点

7、最快时间内找到单链表倒数第K个节点?

8、最快时间内删除单链表倒数第K个节点?

 9、 求两个顺序表的交集、并集和差集

10、求两个有序单链表的交集、并集和差集

11、求两个单链表(可以无序)的交集、并集和差集


1、逆置单链表

/*
**思路:
**采用头插法将单链表中每一个元素都插入头结点之后即可实现逆置
**结束后将逆置后的单链表的最后一个元素的next指针置NULL
*/
void Reverse(List plist)
{
    Node *p = plist->next;
    plist->next = NULL;
    while (p != NULL)
    {
        Node *pNext = p->next;
        p->next = plist->next;
        plist->next = p;
        p = pNext;
    }
}

Node* reverse(Node *head)
{
    Node* curNode = head;
    p = CurNode->next;
    
    while(p)
    {
        Node *pNext = p->next;
        p->next = curNode;
        CurNode = p;
        p = pNext;
    }
    head->next = nullptr;
    head = curNode;
    return head;
}

2、反转单链表

/*
**思路:
**设置三个指针分别指向上一位置(prev)、当前位置(cur)、下一位置(pNext)
**反转结束后变为不含头结点的单链表,要重新写show函数输出
*/
Node *Reverse_1(List plist)
{
    Node *ReverseHead = NULL;
    Node *prev = NULL;
    Node *cur = plist;
    while (cur != NULL)
    {
        Node *curNext =cur->next;
        if (curNext == NULL)
        {
            ReverseHead = cur;
        }
        cur->next = prev;
        prev = cur;
        cur = curNext;
    }
    return ReverseHead;
}

void Show_1(List plist)
{
    Node *p = plist;
    while (p->next != NULL)
    {
        printf("%d ",p->data);
        p = p->next; 
    }
    printf("\n");
}

3、合并两个有序的单链表

Node Merge_list(List La,List Lb)
{
    Node Lc;
    InitList(&Lc);
    List p1 = La->next;
    List p2 = Lb->next;
    List p3 = &Lc;
    while (p1 != NULL && p2 != NULL)
    {
        if (p1->data <= p2->data)
        {
            p3->next = p1;
            p3 = p1;
            p1 = p1->next;
        }
        else
        {
            p3->next = p2;
            p3 = p2;
            p2 = p2->next;
        }
    }
    if (p1 != NULL)
    {
        p3->next = p1;
    }
    if (p2 != NULL)
    {
        p3->next = p2;
    }
    return Lc;
}


/*
**递归:
**若链表1的头节点的值小于链表2的头节点的值,则链表1的头节点是合并后的链表的头节点
**再剩余的节点中,链表2的头节点大于链表1的头节点的值,则链表2 的头节点是剩余节点的头节点
**得到两个链表中值较小的头节点并把它链接到已经合并的链表之后,两个链表的剩余节点仍然是排序的
**因此合并步骤和之前是一样的,使用递归实现
*/
Node* Merge_list(Node *p1, Node *p2)
{
    if (p1 == NULL)
    {
        return p2;
    }
    else if(p2 == NULL)
    {
        return p1;
    }
    Node *p3 = NULL;
    if (p1->data < p2->data)
    {
        p3 = p1;
        p3->next = Merge_list(p1->next,p2);
    }
    else
    {
        p3 = p2;
        p3->next = Merge_list(p1,p2->next);
    }
    return p3;
}

4、判断单链表是否有环?环的入口点?环的长度?

/*
**思路:
**判断单链表是否有环:设置两个快慢指针,一个指向plist,
**一个指向plist的下一个元素,若两个快慢指针相遇,则单链表有环
**求环的长度:找到快慢指针的相遇点,从相遇点之后开始遍历,
**并设置计数器,当指针再次等于相遇点时,返回计数器长度即可
**环的入口点:设置两个指针p1,p2,先让p1向前走环的长度,p2
**从头开始遍历,当p1和p2相等时,即找到了环的入口节点
*/

void CreateLoop(List plist)   // 创建一个带环的单链表
{
    Node *p = plist;
    while (p->next != NULL)
    {
        p = p->next;
    }
    p->next = plist->next->next->next;
    }

Node* IsLoop(List plist)
{
    Node *pslow = plist->next;
    Node *pfast = pslow->next;
    while (pfast != NULL && pslow != NULL)
    {
        if (pfast == pslow)
        {
            return pfast;
        }
        pslow = pslow->next;
        pfast = pfast->next;
        if(pfast != NULL)
        {
            pfast = pfast->next;
        }
    }
    return NULL;
}

int LoopLen(List plist)
{
    Node *loop = IsLoop(plist);
    Node *p = loop;
    if (p == NULL)
    {
        return NULL;
    }
    int len = 1;
    while (p->next != loop)
    {
        len++;
        p = p->next;
    }
    return len;
}

Node *Loopstart(List plist)
{
    Node *p1 = plist;
    Node *p2 = plist;
    int len = LoopLen(plist);
    for (int i = 0; i < len; i++)
    {
        p1 = p1->next;
    }
    while (p1 != p2)
    {
        p1 = p1->next;
        p2 = p2->next;
    }
    return p1;
}

5、判断两个单链表是否相交?交点?

/*
**思路:
**求第一个交点:两个指针p1 p2   先求出两个单链表的长度之差 pos ,
**使p1指向较长的单链表头节点,然后p1向前走pos,p2指向较短的
**单链表,从头开始遍历,当p1 = p2,则相交且为两个相交的单链表的第一个交点。
*/

void CreatCut(List plist1,List plist2)   // 创建一个相交的单链表
{
    plist1->next->next = plist2->next->next->next;
}

bool IsCut(List plist1,List plist2)
{
    int len1 = GetLength(plist1);
    int len2 = GetLength(plist2);
    Node *p1 = plist1;
    Node *p2 = plist2;
    int pos = len1 - len2;
    if (pos < 0)
    {
        p1 = plist2;
        p2 = plist1;
        pos = len2 - len1;
    }
    for (int i = 0; i < pos; i++)
    {
        p1 = p1->next;
    }
    while (p1 != NULL && p2 != NULL && p1 != p2)
    {
        p1 = p1->next;
        p2 = p2->next;
    }
    if (p1 == p2 && p1 != NULL)
    {
        return true;
    }
    return false;
}

Node *FirstNode(List plist1,List plist2)
{
    int len1 = GetLength(plist1);
    int len2 = GetLength(plist2);
    Node *p1 = plist1;
    Node *p2 = plist2;
    int pos = len1 - len2;
    if (pos < 0)
    {
        p1 = plist2;
        p2 = plist1;
        pos = len2 - len1;
    }
    for (int i = 0; i < pos; i++)
    {
        p1 = p1->next;
    }
    while (p1 != NULL && p2 != NULL && p1 != p2)
    {
        p1 = p1->next;
        p2 = p2->next;
    }
    if (p1 == p2 && p1 != NULL)
    {
        return p1;
    }
    return NULL;
}

6、O(1)时间删除单链表的一个节点

/*
**思路:
**要删除节点 i,先把 i 的下一个节点 j 的内容复制到 i,
**然后把 i 的指针指向节点 j 的下一个节点,此时再删除j节点,其效果刚好是把节点i删除了
**如果要删除的节点位于链表的尾部,要从链表的头节点开始,顺序遍历得到该节点的前序节点,
**并完成删除操作如果链表中只有一个节点,并要删除链表的头节点(也是尾节点),删除后,
**要将链表的头节点设置为NULL
*/

void DeleteNode(List plist,List pDel)
{
    if (pDel->next != NULL)
    {
        Node *pDelNext = pDel->next;
        pDel->next = pDelNext->next;
        pDel->data = pDelNext->data;
        free(pDelNext);
        pDelNext = NULL;
    }
    else
    {
        Node *p = plist;
        while (p->next != pDel)
        {
            p = p->next;
        }
        p->next = NULL;
    }
}

7、最快时间内找到单链表倒数第K个节点?

/*
**思路:
**设置两个指针p1和p2,p1向前走k-1步后,p1和p2同时向前遍历,
**当p1走向尾节点时,p所在位置即倒数第k个节点
*/
Node *Lask_K(List plist,int k)
{
    Node *p1 = plist;
    Node *p2 = plist;
    for (int i = 0; i < k-1; i++)
    {
        if (p1->next != NULL)
        {
            p1 = p1->next;
        }
        else
        {
            return NULL;
        }
    }
    while (p1->next != NULL)
    {
        p1 = p1->next;
        p2 = p2->next;
    }
    return p2;
}

8、最快时间内删除单链表倒数第K个节点?

void DelLask_K(List plist,int k)
{
    assert(plist != NULL);
    Node *p1 = plist;
    Node *p2 = plist;
    for (int i = 0; i < k; i++)
    {
        if (p1->next != NULL)
        {
            p1 = p1->next;
        }
        else
        {
            return;
        }
    }
    while (p1->next != NULL)
    {
        p1 = p1->next;
        p2 = p2->next;
    }
    Node *pDel = p2->next;
    p2->next = pDel->next;
    free(pDel);
    pDel = NULL;
}

 9、 求两个顺序表的交集、并集和差集

/*
** 思路(交集):
** 新开辟一个顺序表存放交集
** 如果遍历两个顺序表,查找相同元素写入交集中,并记录长度
*/
void Intersection(PSqlist sq1,PSqlist sq2,PSqlist sq3)
{
	int k = 0;
	for (int i = 0; i < sq1->length; i++)
	{
		int j = 0;
		while(j < sq2->length && sq2->elem[j] != sq1->elem[i])
		{
			j++;
		}
		if (j < sq2->length)
		{
			sq3->elem[k++] = sq2->elem[j];
		}
	}
	sq3->length = k;
}

/*
**	思路(并集):
**	新开辟一个顺序表存放并集
**	由于是并集,所以结果中至少包含了一个顺序表中的所有元素,因此首先将第一个顺序表写入并集。
**	然后遍历第二个顺序表,每拿到一个元素都遍历一遍第一个顺序表,进行比对,第一个顺序表遍历完毕
**	第二个顺序表中的该元素在第一个顺序表中依然没有找到与之相等的元素,就将第二个顺序表中的该元素就写入并集
*/

void Union(PSqlist sq1,PSqlist sq2,PSqlist sq4)
{
	for (int i = 0; i < sq1->length; i++)
	{
		sq4->elem[i] = sq1->elem[i];
	}
	sq4->length = sq1->length;

	int k = sq4->length;
	for (int i = 0; i < sq2->length; i++)
	{
		int j = 0;
		while(j < sq1->length && sq1->elem[j] != sq2->elem[i])
		{
			j++;
		}
		if (j == sq1->length)
		{
			sq4->elem[k++] = sq2->elem[i];
		}
	}
	sq4->length = k;
}

/*
**	思路(差集):
**	新开辟一个顺序表存放差集
**	属于A而不属于B的元素的集合称为A与B的差
**	则遍历第一个顺序表,每拿到一个元素都遍历一遍第二个顺序表,进行比对,第二个顺序表遍历完毕
**	第一个顺序表中的该元素在第二个顺序表中依然没有找到与之相等的元素,就将第一个顺序表中的该元素写入差集
*/

void Difference_Set (PSqlist sq1,PSqlist sq2,PSqlist sq5)
{
	int k = 0;
	for (int i = 0; i < sq1->length; i++)
	{
		int j = 0;
		while(j < sq2->length && sq1->elem[i] != sq2->elem[j])
		{
			j++;
		}
		if (j == sq2->length)
		{
			sq5->elem[k++] = sq1->elem[i];
		}
	}
	sq5->length = k;
}

10、求两个有序单链表的交集、并集和差集

两个有序的无头结点的链表La,Lb,求二者的交集、并集、差集,并且把结果分别存在一个新链表中返回。

/*
**	思路(交集):
**	依次遍历两个链表,比较两个链表当前元素的大小关系
**	如果两个链表当前元素相等,则找到一个相交元素
**	如果第一个链表元素小于第二个链表元素,则第一个链表的指针后移一位
**	如果第一个链表元素大于第二个链表元素,则第二个链表的指针后移一位
*/ 

BNode *Intersection(BTlist plist1,BTlist plist2)
{
	BTlist Lc;
	Lc = NULL;  // 初始化无头单链表
	BTlist p1 = plist1;
	BTlist p2 = plist2;
	BTlist p3 = Lc;
	while(p1 != NULL && p2 != NULL)
	{
		if (p1->data < p2->data)
		{
			p1 = p1->next;
		}
		else if(p1->data > p2->data)
		{
			p2 = p2->next;
		}
		else   // 使用尾插法将交集存入新链表
		{
			BNode *pGet = (BNode *)malloc(sizeof(BNode));
			pGet->data = p2->data;
			pGet->next = NULL;

			if (p3 == NULL)
			{
				Lc = pGet;  // 第一个交集元素节点存入Lc
				p3 = Lc;
			}
			else
			{
				while (p3->next != NULL)
				{
					p3 = p3->next;
				}
				p3->next = pGet;
			}

			//Insert_tail(&Lc,p2->data);
			p1 = p1->next;
			p2 = p2->next;
		}
	}
	return Lc;
}


/*
**	思路(并集):
**	依次遍历两个链表,比较两个链表当前元素的大小关系
**	如果第一个链表元素小于第二个链表元素,则第一个链表当前元素存入新链表,并后移第一个链表指针
**	如果第一个链表元素大于第二个链表元素,则第二个链表当前元素存入新链表,并后移第二个链表指针
**  如果第一个链表元素等于第二个链表元素,则第一个链表当前元素存入新链表,并后移第一个和第二个链表指针
**	如果两个链表中某个还有剩余,则将剩余元素插入新链表
*/ 

BNode *Union(BTlist plist1,BTlist plist2)
{
	BTlist Lc;
	Lc = NULL;  // 初始化无头单链表
	BTlist p1 = plist1;
	BTlist p2 = plist2;
	while(p1 != NULL && p2 != NULL)
	{
		if (p1->data < p2->data)
		{
			Insert_tail(&Lc,p1->data);
			p1 = p1->next;
		}
		else if(p1->data > p2->data)
		{
			Insert_tail(&Lc,p2->data);
			p2 = p2->next;
		}
		else
		{
			Insert_tail(&Lc,p1->data);
			p2 = p2->next;
			p1 = p1->next;
		}
	}
	while (p1 != NULL)
	{
		Insert_tail(&Lc,p1->data);
		p1 = p1->next;
	}
	while (p2 != NULL)
	{
		Insert_tail(&Lc,p2->data);
		p2 = p2->next;
	}
	return Lc;
}


/*
**	思路(差集):
**	依次遍历两个链表,比较两个链表当前元素的大小关系
**	如果两个链表当前元素相等,则两个链表的指针均后移一位
**	如果第一个链表元素小于第二个链表元素,则第一个链表的元素肯定是独有的,所有加入到结果链表中 
**	如果第一个链表元素大于第二个链表元素,则第二个链表的指针后移一位
		 	
*/ 

BNode *Difference_Set(BTlist plist1,BTlist plist2)
{
	BTlist Lc;
	Lc = NULL;  // 初始化无头单链表
	BTlist p1 = plist1;
	BTlist p2 = plist2;
	while(p1 != NULL && p2 != NULL)
	{
		if (p1->data < p2->data)
		{
			Insert_tail(&Lc,p1->data);
			p1 = p1->next;
		}
		else if(p1->data > p2->data)
		{
			p2 = p2->next;
		}
		else
		{
			p1 = p1->next;
			p2 = p2->next;
		}
	}
	while (p1 != NULL)
	{
		Insert_tail(&Lc,p1->data);
		p1 = p1->next;
	}
	return Lc;
}

11、求两个单链表(可以无序)的交集、并集和差集

bool IsPresent(BTlist plist,int elem)
{
	BTlist p = plist;
	while(p != NULL)
	{
		if (p->data == elem)
		{
			return true;
		}
		p = p->next;
	}
	return false;
}


/*
**	思路(交集):
**	依次遍历第一个链表,在第二个链表中查找该元素
**  如果第二个链表中也有该元素,则将该元素插入到结果链表中。	 	
*/ 
BNode *Intersection_1(BTlist plist1,BTlist plist2)
{
	BTlist Lc;
	Lc = NULL;  // 初始化无头单链表
	BTlist p1 = plist1;
	BTlist p2 = plist2;
	
	while(p1 != NULL)
	{
		if (IsPresent(p2,p1->data))
		{
			Insert_tail(&Lc,p1->data);
		}
		p1 = p1->next;
	}
	return Lc;
}


/*
**	思路(并集):
**	现将第一个链表全部插入结果链表中
**	然后遍历第二个链表,若第二个链表中的某元素在结果链表中不存在
**  则将该元素插入到结果链表中。	 	
*/ 
BNode *Union_1(BTlist plist1,BTlist plist2)
{
	BTlist Lc;
	Lc = NULL;  // 初始化无头单链表
	BTlist p1 = plist1;
	BTlist p2 = plist2;
	
	while(p1 != NULL)
	{
		Insert_tail(&Lc,p1->data);
		p1 = p1->next;
	}

	while(p2 != NULL)
	{
		if(!IsPresent(Lc,p2->data))
		{
			Insert_tail(&Lc,p2->data);
		}
		p2 = p2->next;
	}
	return Lc;
}


/*
**	思路(差集):
**	遍历第一个链表,若该元素在第二个链表中不存在,则将该元素插入到结果链表中。	 	
*/ 
BNode *Difference_Set_1(BTlist plist1,BTlist plist2)
{
	BTlist Lc;
	Lc = NULL;  // 初始化无头单链表
	BTlist p1 = plist1;
	BTlist p2 = plist2;

	while(p1 != NULL)
	{
		if(!IsPresent(p2,p1->data))
		{
			Insert_tail(&Lc,p1->data);
		}
		p1 = p1->next;
	}
	return Lc;
}

 

你可能感兴趣的:(GS—C,C,程序设计)