左神算法笔记02:哈希表、有序表、链表

哈希表

基本概念不做阐述
增删改查的时间复杂度都是常数级别(但是比较大的常数)

  1. java中,如果哈希表的key是基本数据类型,哈希表内部按真实值传递
  2. java中,如果哈希表的key不是基本数据类型,哈希表内部按地址值传递(8字节)

有序表

  1. 有序表在使用层面上可以理解为一种集合结构
  2. 如果只有key,没有伴随数据value,可以使用TreeSet结构
  3. 如果既有key,又有伴随数据value,可以使用TreeMap结构
  4. 有无伴随数据,是TreeSet和TreeMap唯一的区别,底层实现是一回事
  5. 有序表和哈希表的区别是,有序表把key按照顺序组织起来,而哈希表完全不组织
  6. 红黑树、AVL树、size-balance-tree和跳表等都属于有序表结构,只是底层具体实现不同
  7. 放入有序表的东西,如果是基础类型,内部按值传递,内存占用就是这个东西的大小
  8. 放入有序表的东西,如果不是基础类型,必须提供比较器,且内部按地址值传递,内存占用的就是这个东西的内存地址大小
  9. 不管底层用什么实现,只要是有序表,都有固定的基本功能和固定的时间复杂度
    在这里插入图片描述

在这里插入图片描述

链表

单链表
面试时链表解题方法论
对于笔试,不用太在乎空间复杂度,一切为了时间复杂度
对于面试,时间复杂度依然放在第一位,但是一定要找到空间最省的方法

重要技巧:
额外数据结构记录(哈希表)
快慢指针

题目

反转单向和双向链表

题目:分别实现反转单向链表和反转双向链表的函数
要求:如果链表长度为N,时间复杂度要求为O(N),额外空间复杂度为O(1)

打印两个有序链表的公共部分

题目:给定两个有序链表的头指针head1和head2,打印两个链表的公共部分
要求:如果两个链表的长度之和为N,时间复杂度要求为O(N),额外空间复杂度为O(1)

判断一个链表是否是回文结构

题目:给定一个单链表的头结点head,请判断该链表是否为回文结构
例子:1->2->1,返回true;1->2->2->1,返回true;15->6->15,返回true;1->2->3,返回false;
要求:如果链表长度为N,时间复杂度要求为O(N),额外空间复杂度为O(1)

方法1:笔试利用栈来做,遍历一遍链表,把链表元素入栈;再遍历一遍链表,栈弹出元素并与原链表当前元素对比,不一样就返回false

方法2:利用快慢指针,快指针一次走两步,慢指针一次走一步,当快指针到最后的时候,慢指针刚好到中间,这时候利用栈将链表后半部分的元素入栈,再弹出比较,从而节省了一半空间

方法3:利用快慢指针,快指针到最后的时候,慢指针刚好到中点,然后利用慢指针,将链表后半部分逆序,即1->2变成2->1,再用两个指针,一个从头开始遍历,一个从尾开始遍历,有不同则返回false,否则结束后返回true,最后记得恢复原数组

将单向链表按某值划分成左边小,中间相等,右边大的形式

题目:给定一个单链表的头结点head,节点的值类型是整型,再给定一个整数pivot,实现一个调整链表的函数,将链表调整为左部分都是值小于pivot的节点,中间部分都是值等于pivot的节点,右部分都是值大于pivot的节点

进阶:在实现原问题功能的基础上,增加如下要求
要求:调整后所有小于pivot的节点之间的相对顺序和调整前一样
要求:调整后所有等于pivot的节点之间的相对顺序和调整前一样
要求:调整后所有大于pivot的节点之间的相对顺序和调整前一样
要求:时间复杂度达到O(N),额外空间复杂度为O(1)

方法1:利用node数组,将链表全部节点方法数组中,然后进行partation,但是好像保持不了相对顺序

方法2:利用6个变量,分别指向小于区的头,小于区的尾,等于区的头,等于区的尾,大于区的头,大于区的尾;通过遍历,可以将所有元素对应哪个区域找到,并更新各个变量的值,最后再将小于区的尾和等于区的头连接,等于区的尾和大于区的头连接,返回小于区的头(一定要讨论是否有小于区,等于区,大于区)

复制含有随机指针节点的链表

题目:一种特殊的单链表节点类描述如下:
在这里插入图片描述
rand指针是单链表节点结构中新增的指针,rand可能指向链表中的任意一个节点,也可能指向null。给定一个由node节点类型组成的无环单链表的头结点head,请实现一个函数完成这个链表的复制,并返回复制的新链表的头结点
要求:时间复杂度O(N),额外空间复杂度O(1)

两个单链表相交的一系列问题

题目:给定两个可能有环也可能无环的单链表,头结点head1和head2。请实现一个函数,如果两个链表相交,请返回相交的第一个节点。如果不想交,返回null
要求:如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度为O(1)

步骤一:先判断一个链表是否有环,如果有,返回头结点
方法1:使用hash表遍历
方法2:利用快慢指针
在这里插入图片描述

	public static Node getLoopNode(Node head) {
		if (head == null || head.next == null || head.next.next == null) {
			return null;
		}
		Node n1 = head.next; // n1 -> slow
		Node n2 = head.next.next; // n2 -> fast
		while (n1 != n2) {
			if (n2.next == null || n2.next.next == null) {
				return null;//无环
			}
			n2 = n2.next.next;
			n1 = n1.next;
		}
		n2 = head; // n2 -> walk again from head
		while (n1 != n2) {
			n1 = n1.next;
			n2 = n2.next;
		}
		return n1;
	}

步骤二:讨论head1和head2在不同情况下的处理方法
情况1:loop1 == null && loop2 == null
在这里插入图片描述

 public static Node noLoop(Node head1, Node head2) {
		if (head1 == null || head2 == null) {
			return null;
		}
		Node cur1 = head1;
		Node cur2 = head2;
		int n = 0;
//首先得到两个链表的最后一个节点,如果不相等,那肯定 不相交,相等,先走长的链表将两个链表的差值走完,然后一起走,找
		while (cur1.next != null) {//当cur1变成最后一个就出来了
			n++;//cur的总数
			cur1 = cur1.next;
		}
		while (cur2.next != null) {
			n--;//得到差值
			cur2 = cur2.next;
		}
		if (cur1 != cur2) {//如果两个无环链表的最后一个节点不相等,那么不相交
			return null;
		}
		cur1 = n > 0 ? head1 : head2;//谁长谁的head变成cur1
		cur2 = cur1 == head1 ? head2 : head1;//谁短谁的head变成cur2
		n = Math.abs(n);
		while (n != 0) {//先走两个链表的差值
			n--;
			cur1 = cur1.next;
		}
		while (cur1 != cur2) {//然后一起走,第一次相交的时候就是那个点
			cur1 = cur1.next;
			cur2 = cur2.next;
		}
		return cur1;
	}

情况2:loop1,loop2中有null也有非null,则一定不相交
情况3:loop1,loop2都非空,则分三种情况
在这里插入图片描述

	public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
		Node cur1 = null;
		Node cur2 = null;
		if (loop1 == loop2) {
			cur1 = head1;
			cur2 = head2;
			int n = 0;
			while (cur1 != loop1) {
				n++;
				cur1 = cur1.next;
			}
//情况2),和单链表的相交差不多,原先是遇到 null结尾处终止 ,现在是loop处就终止
			while (cur2 != loop2) {
				n--;
				cur2 = cur2.next;
			}
			cur1 = n > 0 ? head1 : head2;
			cur2 = cur1 == head1 ? head2 : head1;
			n = Math.abs(n);
			while (n != 0) {
				n--;
				cur1 = cur1.next;
			}
			while (cur1 != cur2) {
				cur1 = cur1.next;
				cur2 = cur2.next;
			}
			return cur1;
		} else {//看能不能遇到loop2
			cur1 = loop1.next;
			while (cur1 != loop1) {//如果都转回自己,还没有遇到loop2,就是情况1)
				if (cur1 == loop2) {
					return loop1;
				}
				cur1 = cur1.next;
			}
			return null;
		}
	}

主函数调用

public static Node getIntersectNode(Node haed1,Node head2){
	if(head1 == null || head2 == null){
		return null;
	}
	Node loop1 = getLoopNode(head1);
	Node loop2 = getLoopNode(head2);
	if(loop1 == null && loop2 == null){
		return noLoop(head1, head2);
	}
	if(loop1 != null && loop2 != null){
		return bothLoop(head1, loop1, head2, loop2);
	}
	return null;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值