【蓝蓝计算机考研算法】-day25-去掉链表重复值、对称

125 阅读4分钟

37.递增的单链表,去掉表中重复的元素,只保留一个数值

示例

输入:1 -> 2 -> 2 -> 3 -> 3->4->5-> 5 输出:1->2->3->4->5
说明:在递增的单链表中,重复的元素包括 2,3 和 5,删除后只保留了一个数值,得到的单链表为 1->2->3->4->5。

思路

1. 递增有序:
因为链表是递增有序的,所以默认第一个是不重复的元素,将第一个与下一个进行比较,若相等则用临时指针指住下一个值的位置,将当前指针指向下下个位置,完成删除。
2.对于不是递增有序的

  • 遍历链表,从虚拟头节点开始,每次判断下一个节点,是否为首次出现,(其中recorde[val] == 0表示首次出现,否则不是),如果是首次出现就将该节点记录在辅助数中record中,否则删除该节点。
  • 每次判断下一个节点,是否为首次出现,而不是判断当前节点,是为了方便删除操作,这样每次删除一个节点的时候,我们都可以快速的拿到该节点的前驱节点。
  • 辅助数组中record[key] = val,其中key为节点的数据,val为该节点出现的次数。
void DelDupNode(LinkList& L) {
    LNode* cur = L, * tmp;           // 遍历指针,临时指针
    unordered_map<int, int> record; // 辅助数组,记录各个节点出现的次数
    while (cur->next) {
        int val = cur->next->val; // 数据域
        if (record[val] == 0) { // 对于第一次出现的节点,把它记录到辅助数组中
            record[val] = 1;
            cur = cur->next;
        }
        else {
            tmp = cur->next;             // 重复节点
            cur->next = cur->next->next; // 防止断链
            delete tmp;                  // 删除重复节点
        }
    }
}
int main() {
  LNode* cur = L->next;
cout << "去重后的链表:";
while (cur) {
    cout << cur->val << ' ';
    cur = cur->next;
}
}

代码实现

//删除重复元素
#include <stdio.h>
#include <stdlib.h>
//删除重复元素
typedef struct LNode { //定义链表的结构体
	int data;          //数据域
	struct LNode* next;//指针域
} LNode, * LinkList;
//创建链表-尾插法
LinkList ListInsert(int n) {					// n代表链表的结点个数
	LNode* L = (LinkList)malloc(sizeof(LNode)); //创建头结点
	L->next = NULL;								//初始为空链表
	LNode* s, * r = L; // s是待插入的结点指针,r是尾指针
	int val;		  //待插入的结点数据
	while (n-- && scanf_s("%d", &val)) {
		s = (LNode*)malloc(sizeof(LNode)); //创建新结点
		s->data = val;
		r->next = s; // r链接s
		r = s;		// r指向新的表尾结点
	}
	printf("创建成功\n");
	return L->next; // 返回真正的头节点
}

//删除重复的值
LNode* DeleteDupList(LNode* L) {
	// 如果链表为空或只有一个节点,直接放回原链表
	if (L == NULL || L->next == NULL) {
		return L;
	}
	LNode* p = L->next, * temp; //移动指针p指向头结点,temp临时指针
	while (p->next != NULL) {
		temp = p->next;				// temp指向*p的后继结点
		if (p->data == temp->data) { //找到重复的结点
			p->next = temp->next;	//释放*temp结点
			free(temp);

		}
		else {
			p = p->next;
		}
	}
	return L;
}
int main() {
	//创建链表
	int n;
	printf("请输入链表长度:");
	scanf_s("%d", &n);
	printf("请输入%d个数据,递增有序:", n);
	LinkList L = ListInsert(n); //创建链表 [,测试数据[1,1,2],直接将n替换为3]
	// 输出去重后的链表
	LNode* newL = DeleteDupList(L);
	LNode* cur = newL;
	printf("删除重复元素后的序列:");
	while (cur != NULL) {
		printf(" %d", cur->data);
		cur = cur->next;
	}
	return 0;
}

运行结果

vs2022上运行时总报错:

image.png
别人可运行出来

39248cacadcd1737d10f0524fee271a.png

复杂度

  • 时间复杂度O(n)--- 其中n是链表的长度。因为链表中的每个节点都只会被遍历一次。
  • 空间复杂度 O(1)--- 单链表为必要空间仅需常数级变量

38.判断带头结点的循环双链表是否对称

示例

输入:1 2 2 1
输出:这个是对称的
输入:1 2 3 1 2
输出 :这个不是对称的

思路

让P从左向右扫描,q从右向左扫描,直到他们指向同一结点(p==q,当循环双链表中节点个数等于奇数时)或相邻(q->prior=p,当循环双链表中节点个数为偶数时)为止,若他们指向结点值相同,则继续下去,否则返回false,.若比较全部相等,,则返回true.

代码实现

//双链表是否对称
#include<stdio.h>
#include<stdlib.h>
// 定义双链表节点结构体
typedef struct DNode {
    int data;
    struct DNode* next,*prior;//后继指针和前驱指针
}DNode,*DLinkList;

//从两头扫描循环双链表,以判断链表是否对称
bool symmetry(DLinkList L) {
    DNode* p = L->next, * q = L->prior;//p指向头指针,q指向尾指针
    while (p != q && p->next != q) {
        if (p->data == q->data) {//所指结点值相同则继续比较
            p = p->next;
            q = q->prior;
        }
        else {
            return false;
        }
        return true;
    }
   
}
int main() {
    // 创建循环双链表测试输入:1 2 2 1输出:这个是对称的
    DNode* L = (DNode*)malloc(sizeof(DNode)); // 虚拟头节点,不存储实际的数据
    DNode* n1 = (DNode*)malloc(sizeof(1));
    DNode* n2 = (DNode*)malloc(sizeof(2));
    DNode* n3 = (DNode*)malloc(sizeof(2));
    DNode* n4 = (DNode*)malloc(sizeof(1));
    L->next = n1;
    n1->prior = L;
    n1->next = n2;
    n2->prior = n1;
    n2->next = n3;
    n3->prior = n2;
    n3->next = n4;
    n4->prior = n3;
    n4->next = L;
    L->prior = n4;

    // 判断链表是否对称
    if (symmetry(L)) {
       printf( "这个是对称的" );
    }
    else {
        printf("这个不是对称的");
    }
    return 0;
}

运行结果

image.png

复杂度

  • 时间复杂度O(n)--- 其中n是链表的长度。因为链表中的每个节点都只会被遍历一次。
  • 空间复杂度O(1)---双链表为必要空间,除此仅需常数级变量