数据结构-链表及相关算法

// 单链表节点的结构
public class ListNode {
   
    int val;
    ListNode next;
    ListNode(int x) {
    val = x; }
}

链表

  1. 链表是以节点的方式来存储,是链式存储
  2. 每个节点包含data 域,next域:指向下一个节点.
  3. 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定.

基础知识

哈希表的简单介绍

  1. 哈希表在使用层面上可以理解为一种集合结构
  2. 如果只有key,没有伴随数据value,可以使用HashSet结构(C++中叫UnOrderedSet)
  3. 如果既有key,又有伴随数据value,可以使用HashMap结构(C++中叫UnOrderedMap)
  4. 有无伴随数据,是HashMap和HashSet唯一的区别,底层的实际结构是一回事
  5. 使用哈希表增(put)、删(remove)、改(put)和查(get)的操作,可以认为时间复杂度为
    O(1),但是常数时间比较大
  6. 放入哈希表的东西,如果是基础类型,内部按值传递,内存占用就是这个东西的大小
  7. 放入哈希表的东西,如果不是基础类型,内部按引用传递,内存占用是这个东西内存地
    址的大小

在java中 HashSet就是一个只使用HashMap的key的一个集合。

有序表的简单介绍

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

有序表的固定操作:

  1. void put(K key, V value):将一个(key,value)记录加入到表中,或者将key的记录
    更新成value。
  2. V get(K key):根据给定的key,查询value并返回。
  3. void remove(K key):移除key的记录。
  4. boolean containsKey(K key):询问是否有关于key的记录。
  5. K firstKey():返回所有键值的排序结果中,最左(最小)的那个。
  6. K lastKey():返回所有键值的排序结果中,最右(最大)的那个。
  7. K floorKey(K key):如果表中存入过key,返回key;否则返回所有键值的排序结果中,
    key的前一个。
  8. K ceilingKey(K key):如果表中存入过key,返回key;否则返回所有键值的排序结果中,key的后一个。
    以上所有操作时间复杂度都是O(logN),N为有序表含有的记录数

练习

反转单链表和双链表

节点结构:

public class TreeNode {
   
	public int num;
	public TreeNode next;
	public TreeNode last;
	public TreeNode(int num) {
   
		this.num = num;
	}
	public TreeNode() {
   }
}
public class Node {
   
	public int num;
	public Node next;
	public Node(int num, Node next) {
   
		this.num = num;
		this.next = next;
	}
}

两种方式反转链表结构:

/**
	 * 反转单向链表
	 * while循环
	 */
	public static Node reversal1(Node head) {
   
		Node last = head.next;
		head.next = null;
		while (last != null) {
   
			Node temp = last.next;
			last.next = head;
			head = last;
			last = temp;
		}
		return head;
	}

	/**
	 * 反转单链表
	 * 递归
	 * @param head
	 * @return
	 */
	public static Node reversal2(Node head) {
   
		if(head.next == null) {
   
			return head;
		}
		Node last = reversal2(head.next);
		head.next.next = head;
		head.next = null;
		return last;
	}


	/**
	 * 反转双向链表
	 * 循环
	 * @param head
	 * @return
	 */
	public static TreeNode reversal3(TreeNode head) {
   
		TreeNode next = head.next;
		head.next = null;
		while (next != null) {
   
			head.last = next;
			TreeNode temp = next.next;
			next.next = head;
			head = next;
			next = temp;
		}
		return head;
	}
	/**
	 * 反转双向链表
	 * 递归
	 * @param head
	 * @return
	 */
	public static TreeNode reversal4(TreeNode head) {
   
		if (head.next == null) {
   
			return head;
		}
		TreeNode node = reversal4(head.next);
		head.next.last = head.next.next;
		head.next.next = head;
		head.next = null;
		return node;
	}

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

public static void portion(Node n1, Node n2) {
   
		while(n1 != null && n2 != null) {
   
			if (n1.num == n2.num) {
   
				System.out.print(n1.num);
				n1 = n1.next;
				n2 = n2.next;
			}else if (n1.num < n2.num) {
   
				n1 = n1.next;
			}else if (n1.num > n2.num) {
   
				n2 = n2.next;
			}
		}
	}

技巧

面试时链表解题的方法论

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

重要技巧:

  1. 额外数据结构记录(哈希表等)
  2. 快慢指针

示例

判断回文链表
/**
	 * 不使用额外的空间,适用于面试
	 * @param head
	 * @return
	 */
	public static boolean plalindrome2(Node head) {
   
		//首先通过快慢指针找到中间的节点,如果是偶数:中间左边,奇数:中间节点
		Node slowNode = head;
		Node quickNode  = head
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值