算法[3] 前缀树、链表相关题目

前缀树、链表相关题目

前缀树

  • 前缀树又名字典树,单词查找树,Trie树,多路树形结构,和hash效率有一拼,是一种用于快速检索的多叉树结构。多用于词频搜索或者模糊查询。
  • 查询时只与样本长度有关,而与样本量无关。
  • 1)单个字符串中,字符从前到后的加到-棵多叉树上
    2)字符放在路上,节点上有专属的数据项(常 见的是pass和end值)
    3)所有样本都这样添加,如果没有路就新建,如有路就复用
    4)沿途节点的pass值增加1,每个字符串结束时来到的节点end值增加1
    可以完成前缀相关的查询

pass 为通过次数,end 为以当前元素结尾的次数

生成这棵树的代价为 O(N)

public class Code_01_TrieTree {
    
    	
	public static class TrieNode {
    
    
		public int path;
		public int end;
		public TrieNode[] nexts;

		public TrieNode() {
    
    
			path = 0;   //有多少个结点到达过
			end = 0;    //有多少个字符串以这个结点结尾
			nexts = new TrieNode[26];  //通向子节点的路,如果题目所给的范围不确定就用map
		}
	}

	public static class Trie {
    
    
		private TrieNode root;

		public Trie() {
    
    
			//准备一个头结点
			root = new TrieNode();
		}

		//将一个单词插入
		public void insert(String word) {
    
    
			if (word == null) {
    
    
				return;
			}
			char[] chs = word.toCharArray();
			TrieNode node = root;
			int index = 0;
			for (int i = 0; i < chs.length; i++) {
    
    
				index = chs[i] - 'a';
				if (node.nexts[index] == null) {
    
    
					node.nexts[index] = new TrieNode();
				}
				node = node.nexts[index];
				node.path++;
			}
			node.end++;
		}

		//在结构中删除这个单词
		public void delete(String word) {
    
    
			if (search(word) != 0) {
    
    
				char[] chs = word.toCharArray();
				TrieNode node = root;
				int index = 0;
				for (int i = 0; i < chs.length; i++) {
    
    
					index = chs[i] - 'a';
					if (--node.nexts[index].path == 0) {
    
    
						//如果某个结点-1之后==0,则说明此节点之后的结点也是-1之后==0,因此直接=null即可。
						node.nexts[index] = null;
						return;
					}
					node = node.nexts[index];
				}
				node.end--;
			}
		}

		//查找某个单词插入了几次
		public int search(String word) {
    
    
			if (word == null) {
    
    
				return 0;
			}
			char[] chs = word.toCharArray();
			TrieNode node = root;
			int index = 0;
			for (int i = 0; i < chs.length; i++) {
    
    
				index = chs[i] - 'a';
				if (node.nexts[index] == null) {
    
    
					return 0;
				}
				node = node.nexts[index];
			}
			return node.end;
		}


		//查某个字符串前缀数量是多少
		public int prefixNumber(String pre) {
    
    
			if (pre == null) {
    
    
				return 0;
			}
			char[] chs = pre.toCharArray();
			TrieNode node = root;
			int index = 0;
			for (int i = 0; i < chs.length; i++) {
    
    
				index = chs[i] - 'a';
				if (node.nexts[index] == null) {
    
    
					return 0;
				}
				node = node.nexts[index];
			}
			return node.path;
		}
	}

	public static void main(String[] args) {
    
    
		Trie trie = new Trie();
		System.out.println(trie.search("zuo"));
		trie.insert("zuo");
		System.out.println(trie.search("zuo"));
		trie.delete("zuo");
		System.out.println(trie.search("zuo"));
		trie.insert("zuo");
		trie.insert("zuo");
		trie.delete("zuo");
		System.out.println(trie.search("zuo"));
		trie.delete("zuo");
		System.out.println(trie.search("zuo"));
		trie.insert("zuoa");
		trie.insert("zuoac");
		trie.insert("zuoab");
		trie.insert("zuoad");
		trie.delete("zuoa");
		System.out.println(trie.search("zuoa"));
		System.out.println(trie.prefixNumber("zuo"));

	}

}


链表

笔试可以不在意空间复杂度,一切为了时间复杂度

面试时,时间复杂度最重要,但要节省空间

::快慢指针::

1)输入链表头节点,奇数长度返回中点,偶数长度返回上中点

2)输入链表头节点,奇数长度返回中点,偶数长度返回下中点

3)输入链表头节点,奇数长度返回中点前一个,偶数长度返回.上中点前一个

4)输入链表头节点,奇数长度返回中点前一个,偶数长度返回下中点前一个.


1)输入链表头节点,奇数长度返回中点,偶数长度返回上中点

static class Node{
    
    
        int val;
        Node next;
    }
    public static Node mid0rUpMidNode(Node head) {
    
    
        if (head == null || head.next == null || head.next.next == null) {
    
    
            return head;
        }
        //
        Node slow = head.next;
        Node fast = head. next. next;
        while (fast.next != null && fast.next.next != null) {
    
    
            slow = slow. next;
            fast = fast .next .next;
        }
        return slow;
    }

    public static Node mid0rUpMidNode2(Node head) {
    
    
        if (head == null|| head.next == null) {
    
    
            return null;
        }
        Node n1 = head;
        Node n2 = head;
        while (n2.next != null && n2.next.next != null) {
    
     // find mid node
            n1 = n1.next; // n1 -》mid
            n2 = n2.next.next; // n2 -> end
        }
        return n1;
    }


2)输入链表头节点,奇数长度返回中点,偶数长度返回下中点

    public static Node mid0rDownMidNode (Node head) {
    
    
        if (head == null|| head.next == null) {
    
    
            return head;
        }
        Node slow = head . next;
        Node fast = head . next;
        while (fast.next != null && fast.next.next != null) {
    
    
            slow = slow.next;
            fast = fast.next .next;
        }
        return slow;
    }


3)输入链表头节点,奇数长度返回中点前一个,偶数长度返回.上中点前一个

    public static Node mid0rUpMidPreNode(Node head) {
    
    
        if (head == null|| head.next == null|| head.next.next == null) {
    
    
            return null;
        }
        Node slow = head;
        Node fast = head. next. next;
        while (fast.next != null && fast.next.next != null) {
    
    
            slow = slow.next;
            fast = fast .next .next;
        }
        return slow;
    }

4)输入链表头节点,奇数长度返回中点前一个,偶数长度返回下中点前一个.

public static Node mid0rUpMidPreNode(Node head) {
    
    
    if (head == nu11|| head.next == nu11|| head.next.next == nu1l) {
    
    
    return nu1l;
    }
	Node slow = head;
	Node fast = head . next. next;
	while (fast.next != nu1l && fast.next.next != nu1l) {
    
    
		slow = slow.next;
		fast = fast .next .next;
	}
    return slow;
}

::给定一个单链表的头节点head,请判断该链表是否为回文结构。::

1)哈希表方法特别简单(笔试用)

2)改原链表的方法就需要注意边界了( 面试用)

利用栈,先遍历一遍压栈,然后在遍历一遍,同时与栈顶元素对比

用更少的空间?

快慢指针求出中点,和上中点

将后半部分压栈,再次出栈与前半部分对比

常数空间?

先快慢指针定位中点和末尾,将中点的next指针指向null

将后半部分指针逆序,然后头尾依次对比,当有一个的指针指向为null,即为回文结构

中间有一个不一样则为非回文

注意返回结果前,需要把后半部分逆序回来.

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

1)把链表放入数组里,在数组上做partition (笔试用)

2)分成小、中、大三部分,再把各个部分之间串起来(面试用)

public static Node listPartition2(Node head, int pivot) {
    
    
Node sH = nu1l; // small head
Node sT = nu1l; // small tail
Node eH = nu1l; // equal head
Node eT = nu1l; // equal tail
Node mH = nu11; // big head !
Node mT = nu1l; // big tail
Node next = nu1l; // save next node
// every node distributed to three lists
while (head != nu1l) {
    
    
    next = head.next;
    head.next = nu1l;
    if (head.value < pivot) {
    
    
        if (sH == null) {
    
    
            sH = head;
            sT = head;
        } else{
    
    
        	sT .next = head;
        	sT = head;
        }else if (head.value == pivot) {
    
    
        	if (eH == nu11) {
    
    
        		eH = head;
        		eT = head;
        	} else {
    
    
        		eT.next = head; 
        		eT = head;
        	}
        }
        head= next;
	}
    
    if (sT != null) {
    
     //如果有小」<域
    	sT .next = eH;
    	eT= eT==null?sT:eT;//谁去连大于区域的头,谁就变成eT
    }
    // 上面的if, 不管跑了没有,et
    // all reconnect
    if (eT != null) {
    
     //如果小于以域和等门<规,不是都没有
    	eT .next = mH;
    }
    	return sH!=null?sH:(eH!=nu1l?eH:mH);

}

拷贝有特殊指针的链表

-种特殊的单链表节点类描述如下
class Node {
Int value.
Node next:
Node rand;
Node(int val){ value= val }
}
rand指针是单链表节点结构中新增的指针,rand可 能指向链表中的任意一个节点,也可能指向mull.
给定一个由ndoe节点类型组成的无环单链表的头节点head.请实现一个函数完成这个链表的复制 并返回复制的新链表的头节点。

[要求]
时间复杂度ON).额外空间复杂度O(1)

建立map

第一遍遍历, key为老节点,value 为新的节点

第二遍遍历链表, 由老节点找到新的节点,并将rand指针赋值给新的节点

最后返回 新节点

public static Node copyListWi thRand1(Node head) {
    
    
    HashMap<Node, Node> map = new HashMap<Node, Node>();
    Node cur = head;
    while (cur != nu1l) {
    
    
        map.put(cur, new Node(cur .value));
        cur = cur .next ;
    }
    cur = head;
    while (cur != nu1l) {
    
    
        .//cur七
        // map.get(cur) 新
        map.get(cur).next = map. get(cur . next);
        map. get(cur).rand = map.get(cur.rand);
        cur = cur .next;
    }
    return map. get(head);
}

不用hash 表 ,实现拷贝

第一次遍历,建立新的节点插入到老节点的后方

第二次遍历设置rand指针,

如果1的rand指向 —> 3

则 3的拷贝节点就在 3后方

将 1的拷贝节点的rand 指针指向3 的后一个节点

最后再将拷贝的节点串联起来返回其头节点

public static Node copyListWithRand2(Node head) {
    
    
    if (head == nu1l) {
    
    
	    return null;
    }
    Node cur = head;
    Node next = null;
    // copy node and link to every node
    //1->2
    //1-> 1’->2
    while (cur != nu11) {
    
    
    //cur老|
    	next = cur .next;
    	cur .next = new Node(cur .value);
    	cur .next.next = next;
    	cur = next;
    	cur = head;
    }
    	
    Node curCopy = nu1l;
    // set copy node rand
    //1->1’->2->2'
    
    while (cur != nu1l) {
    
    
    // cur 2
    // cur.next 新copy
    	next = cur .next .next;
    	curCopy = cur . next;
    	curCopy.rand = cur .rand != null ? cur.rand.next : null;
    	cur = next;
    }
    // head head.next
    Node res = head.next; .
    cur = head;
    // split
    while (cur != nu1l) {
    
    
    	next = cur .next. next;
    	curCopy = cur .next;
    	cur .next = next;
    	curCopy.next = next != nu1l ? next.next : nu11;
	    cur = next;
    }
    return res;
}

返回相交的第一个节点

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

给一个有环链表,找出其入环的节点

//找到链长第个入坏官点。如果无坏。这null
public static Node getLoopNode(Node head) {
    
    
    if (head == nu11 | head.next == nu11|| head.next.next == nu11) {
    
    
    return nu11;
    }
    //n1性n2快
    Node n1 = head.next; // n1 -> slow 
    Node n2 = head.next.next; // n2 -> fast
    while (n1 != n2) {
    
    
    	if (n2.next == null|| n2.next.next == nu1l) {
    
    
    	return nu11;
    	}
    	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;
}

两条链表都无环

第一条链表节点都入set,第二条链表查,查到重复即为相交节点

不用set

遍历第一条链表,记录最后一个节点,且记录链表长度  (长度记作x)

遍历第二条链表,记录最后一个节点,且记录链表长度 (长度记作y)

如果两个最后节点不同,则不相交

x 与y

大的 先开始走, 走x-y步

然后两条链表一起走, 会在相交的节点相遇

//如果两个链长都无坏,驱回第:个相父节点,如果不想灾。驱null
public static Node noLoop(Node head1, Node head2) {
    
    
    if (head1 == null |I head2 == null) {
    
    
    	return nu11;
    }
    Node cur1 = head1;
    Node cur2 = head2;
    intn=0;
    while (cur1.next != nu1l) {
    
    
   	 n++;
   	 cur1 = cur1.next;
    }
    while (cur2.next != nu1l) {
    
    
    	n--;
        cur2 = cur2.next;
    }
    if (cur1 != cur2) {
    
    
    	return nu1l;
    }
    // n :链长1长度减大链 长2长度的值
    cur1=n>0?headl:head2;//谁长,谁的头变成cur1
    cur2 = cur1 == head1 ? head2 : head1; //谁知,谁的头变成cur2
    while(n1=){
    
    
    n--;
    cur1 = cur1.next;
    while (cur1 != cur2) {
    
    
    cur1 = cur1.next;
    cur2 = cur2.next;
    }
    return cur1;
}


一个有环,一个无环,这种情况,两条链表不相交

两个都有环

如果两个有环链表相交,那他们一定是共用这个环

一共有三种情况

如果loop1 的内存地址等于loop2 的内存地址

则为第二种情况

如果loop 1 不等于 loop 2

让loop1 沿着环走,如果没有遇到 loop2 则为第一种情况

也就是没有相交

如果遇到了loop2 就是第三种情况

//两个有环链化。返网第.个相交节点。如果不想交题川null
public static Node bothLoop(Node head1, Node 1oop1, Node head2, Node 1oop2) {
    Node curl = nu1l;
    Node cur2 = null;
    if (loop1 == 1oop2) {
        cur1 = head1 ;
        cur2 = head2;
        intn=0;
        while (cur1 != 1oop1) {
            n++;
            cur1 = cur1.next;
        }
        while (cur2 != 1oop2) {
            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;
        }
    }else {
    cur1 = 1oop1.next;
    while (cur1 != 1oop1) {
        if (cur1 == 1oop2) {
        return loop1;
        }
    cur1 = cur1.next;
    }
    return nu11;
}



主方法

public static Node getIntersectNode (Node head1, Node head2) {
    
    
    if (head1 = null|| head2 = null) {
    
    
    return nu11;
    Node 1oop1 = getLoopNode(head1);
    Node_ 1oop2 = getL oopNode (head2) ;
    if (loop1 == nu11 && loop2 == nu11) {
    
    
    	return noloop(head1, head2); 
    }
    if (loop1 != nu1l && loop2 != nu11) {
    
    
    	return bothLoop(head1, loop1, head2, 1oop2);
    }
    return null;
}

能不能不给单链表的头节点,只给想要删除的节点,就能做到在链表上把这个点删掉?

抖机灵

将删除节点的下一个结点的值赋值给删除节点,然后将next指向下一个节点的下一个节点

但不能删除最后一个节点

猜你喜欢

转载自blog.csdn.net/qq_41852212/article/details/120795238