CC150小结数据结构
来源:互联网 发布:python 图片相似度 库 编辑:程序博客网 时间:2024/05/24 01:15
数组与字符串
二叉堆可以用数组来表示
如大顶堆,构建的过程为从下到上。每次出现子节点大于父节点,旋转,然后对当前节点的子树进行递归处理,打印出排序的堆的时间复杂度为,nlog(n).
1.1确定一个字符串的所有字符是否全都不同。
思路:采用boolean数组进行缓存,ASCII与Unicode,考虑进去长度的因素256个,大于肯定会出现重复,时间复杂度O(n)。
1.2用c或者c++实现void reverse(char* str),反转一个null结尾的字符串。
思路:不使用额外的空间就可以实现,这个是双指针。
这里给出程序
void reverse(char *str){ char *end = str; char temp; if (str){ while(*end){ ++end; } --end; while (str < end){ temp = *str; *str++ = *end; *end-- = temp; } }}
1.3给定两个字符串,请编写程序,确定其中一个字符串重新排列后能否变成另外的一个字符串。
思路:先去定细节,变位词Dog与God疏忽是同一个字符串,空格出现算不算,String.trim()去除首尾的空格。
解法1:可以先对字符串进行排序处理,String.toCharArray(),java.util.Arrays.sort()方法
解法2:假设为ASCII码,那么用int数组记录第一个字串出现的次数,当第二字符串字符出现次数大于第一个出现次数时,返回false。第一个字符串使用toCharArray的方法,第二个使用charAt()。
1.4编写方法,将字符串中的空格全部替换为“%20”。
思路:字符串的操作,这个要进行两次遍历,先进行一次得到数组中空字符串的个数,然后再进行处理。从尾部开始,避免数据丢失。
这里给出代码:
public void replaceSpace(char[] str, int length){ int spacenum = 0, newlength, i; for(i = 0; i < length; i++){ if (str[i] == ' '){ spacenum++; } } newlength = length + spacenum * 2; str[newlength] = '\n';//结束标志位 int index = newlength - 1; for (i = length - 1; i > 0; i--){ if (str[i] != ' '){ str[index--] = str[i]; } else { str[index--] = '0'; str[index--] = '2'; str[index--] = '%'; } }}
1.5利用字符重复出现的次数,编写一个方法,实现基本的字符串的压缩功能。比如,字符串“aabcccccaaa”会变为a2b1c5a3。若压缩后没有变短,就返回原字符串。
思路:采用StringBuffer,按照规则进行统计,最后进行长度统计。
1.6给定一幅N*N矩阵表示的图像,其中每个像素的大小为4个字节,编写一个方法,将图像旋转90度。不占用额外的空间。
思路:进行一层层的旋转,对每一层进行环状旋转。用first来定义每一个环的最小下标,last表示最小标志,offset做缓存。
public static void rotate(int[][] num) { int len = num.length; for (int layer = 0; layer < len / 2; layer++){ int first = layer; int last = len - 1 - first; for (int i = first; i < last; i++){ int offset = i - first; int top = num[first][i]; //左转上 num[first][i] = num[last - offset][first]; //下转左 num[last - offset][first] = num[last][last - offset]; //右转下 num[last][last - offset] = num[i][last]; //上转右 num[i][last] = top; } } }
1.7编写一个方法,若M*N矩阵中某个元素为0,则将其所在的行与列清零。
思路:这是一个陷阱题,首先用两个booelan类型的数组进行归零行以及列的标记,下一步再进行清零操作。
1.8假定有一个方法isSubString,给定两个字符串s1与s2,编写代码检查s2是否由s1旋转组成,只能调用一次isSubString的方法。
思路:out of box方法,假定s1 = xy = waterbottle,x = wat, y = terbottle,s2 = yx =erbottlemat,yx 肯定是xyxy的子串。先进行长度的判定,长度不同,肯定返回false,然后利用子串进行判定。
链表
链表这块需要注意dummy的使用
2.1 编写代码,移除未排序链表中的重复节点。进阶:如果不使用缓冲区,该怎么解决?
思路:利用散列表记录,有一个函数需要掌握,containsKey(),同时注意链表的删除操作,要有previous的缓存。
public void deleteDups(LinkedListNode n){ Hashtable table = new Hashtable(); LinkedListNode previous = null; while (n != null){ if (table.containsKey(n.data)){ previous.next = n.next; } else { table.put(n.data, true); previous = n; } n = n.next; }}
不使用缓存区,就得采用两个指针进行迭代处理
2.2实现一个算法,找出单向链表中倒数第k个结点。
思路:用先行指针的方法
2.3实现一个算法,删除单向链表中间的某个结点,假定你只能访问该结点。
思路:将当前结点的下一个结点的值复制到当前结点,然后删除下一个结点。
2.4编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前。
思路:这个是采用新建两个链表,然后进行合并处理的方式。
java中的super()方法是针对父类来说的。
2.5给定两个用链表表示的整数,每个节点包含一个数位。这些数位是反向存放的也就是个位排在链表的首部。编写函数对这两个整数求和,并利用链表形式返回结果。
示例 : 输入 (7-> 1 -> 6) + ( 5 -> 9 -> 2) ,即617 + 295 与普通的加法保持一致 输出 2-> 1-> 9 ,即912.
这个采用的是普通的加法进行处理。
进阶 :假设这些数位是正向存放的,请再做一遍。
这个是要用到递归进行处理,需要注意两个数的位数不相等。
2.6给定一个有环链表,实现一个算法返回环路的开头结点。
有环链表的定义,在链表中的某个结点的next元素指向在它前面出现过的结点,则表明链表存在环路。
示例
输入: A->B ->C->D->E->C (C结点出现了两次)
那么返回:C 结点
思路:设置两个指针,fast与slow,假设从第s个结点,进入到环路,那么当slow在s结点的时候,fast处于2s结点,让大S为 S = s mod loop_size,此时,fast与slow相差S步,在loop_size - S 步以后,fast 与 slow相遇,碰撞结点距离环路开始的位置为S,(对于slow来说,走了loop_size - S步,那么loop_size- (loop_size- S))此时碰撞结点距离环路起始处的距离为s个结点,链表开始位置也是距离起始处s个结点,那么两个指针方向同时出发,会相交于环路起始结点。
public static LinkedListNode getBegainHead(LinkedListNode head){ LinkedListNode fast = head; LinkedListNode slow = head; while (fast != null && fast.next != null){ slow = slow.next; fast = fast.next.next; if (fast == slow){ break; } } if (fast == null || fast.next == null){ return null; } slow = head; while (slow != fast){ slow = slow.next; fast = fast.next; } return fast; }
这是一个很经典的方法。
2.7 编写一个函数,检查链表是否为回文。
思路:先行指针,取到中间位置的数,用stack来存储数据,注意长度为奇数,根据fast指向的内容即可判断。
栈与队列
3.2请设计一个栈,除push与pop方法,还支持min方法,可返回栈元素中的最小值。push、pop、min方法的时间复杂度为O(1)。
思路:1.新封装 一个对象,对象中包含 value 以及 min,在stack中进行存储。注意super.调用父类方法,peek()查看栈顶元素
2.与以上类似,设计子类,继承Stack,子类中增加新的Stack用于存储出现变化的最小值,入栈操作时进行比较,出栈操作时进行更新。
3.4经典汉诺塔问题
思路:利用stack的数据类型做数据存储
递归方法求解:
public void moveDisks(int n, Tower origin, Tower destination, Tower buffer){ if (n <= 0) return; moveDisks(n -1, origin, buffer, destination); moveTop(origin, destination); moveDisks(n - 1, buffer, destination, origin);}
3.5实现一个MyQuene类,该类用两个栈来实现一个队列
思路:两个栈,一个为new,另外一个为old。队列add数据为new push数据,队列出数据,从old pop数据,如果old 为空,则将new出栈,old入栈。
两个队列实现栈的话,
class MyStack { private Queue<Integer> inquene = new LinkedList<Integer>(); private Queue<Integer> outquene = new LinkedList<Integer>(); int temp =0; // Push element x onto stack. public void push(int x) { inquene.add(x); } // Removes the element on top of the stack. public void pop() { if ( inquene.isEmpty()){ while (!outquene.isEmpty()){ temp = outquene.peek(); outquene.poll(); if( !outquene.isEmpty() ){ inquene.add(temp); } } } else { while (!inquene.isEmpty()){ temp = inquene.peek(); inquene.poll(); if( !inquene.isEmpty() ){ outquene.add(temp); } } } } // Get the top element. public int top() { if( inquene.isEmpty() ){ while( !outquene.isEmpty() ){ temp = outquene.peek(); outquene.poll(); } return temp; }else{ while( !inquene.isEmpty() ){ temp = inquene.peek(); inquene.poll(); outquene.add(temp); } return temp; } } // Return whether the stack is empty. public boolean empty() { return inquene.isEmpty()&&outquene.isEmpty(); }}
3.6经典的stack排序问题,按照升序对栈进行排序。最多只能使用一个额外的栈存放临时数据。
思路:新建一个栈,
//注意给定两个while指针的使用public Stack<Integer> stackSort(Stack<Integer> s1){ Stack<Integer> s2 = new Stack<Integer>(); while (!s1.isEmpty()){ Integer temp = s1.pop(); while(!s2.isEmpty() && s2.peek() > temp){// 本次while循环保证temp是当前s2里面最小的那个 s1.push(s2.pop()); } s2.push(temp);// } return s2;}
树与图
树的三种遍历方式,前序遍历,中序遍历,后序遍历等,递归形式的实现以及使用栈与队列迭代形式的实现。用V表示父节点,L表示左子节点,R表示右子节点,则VLR表示先序遍历,LVR表示中序遍历,LRV表示后序遍历。在处理时,按照递归的思想,对每一个结点都进行顺序上的检查处理。前序,中序,后序是根据中间结点被访问的相对顺序来说的。
判断图是否有环:
针对无向图可以采用并查集来判断是否有环:class GraphCycle{ int V, E; // V-> no. of vertices & E->no.of edges Edge edge[]; // /collection of all edges class Edge {//edges int src, dest; }; // Creates a graph with V vertices and E edges GraphCycle(int v,int e){ V = v; E = e; edge = new Edge[E]; for (int i=0; i<e; ++i) edge[i] = new Edge(); } // A utility function to find the subset of an element i int find(int parent[], int i){ if (parent[i] == -1) return i; return find(parent, parent[i]); } // A utility function to do union of two subsets void Union(int parent[], int x, int y){ int xset = find(parent, x); int yset = find(parent, y); parent[xset] = yset; } // The main function to check whether a given graph // contains cycle or not int isCycle( GraphCycle graph) { // Allocate memory for creating V subsets int parent[] = new int[graph.V]; // Initialize all subsets as single element sets for (int i=0; i<graph.V; ++i) parent[i]=-1; // Iterate through all edges of graph, find subset of both // vertices of every edge, if both subsets are same, then // there is cycle in graph. for (int i = 0; i < graph.E; ++i) { int x = graph.find(parent, graph.edge[i].src); int y = graph.find(parent, graph.edge[i].dest); if (x == y && x != -1) return 1; graph.Union(parent, x, y); } return 0; }
针对有向图,采用的是bfs加上标记位来做 public boolean canFinish(int numCourses, int[][] prerequisites) { HashSet<Integer>[] edges = new HashSet[numCourses]; for (int i = 0 ; i < numCourses; i++) { edges[i] = new HashSet<Integer>(); } for (int[] temp : prerequisites) { HashSet a = edges[temp[0]]; a.add(temp[1]); } for (int i = 0; i < numCourses; i++) { boolean[] visit = new boolean[numCourses]; boolean[] resTack = new boolean[numCourses]; Arrays.fill(visit, false); Arrays.fill(resTack, false); if (isRecyle(i, edges, visit, resTack)) { return false; } } return true; } public boolean isRecyle(int i,HashSet<Integer>[] edges, boolean[] visit, boolean[] resTack) { if (visit[i] == false) { visit[i] = true; resTack[i] = true; HashSet<Integer> pre = edges[i]; for (int id : pre) { if (! visit[id] && isRecyle(id,edges, visit, resTack) ) { return true; } else if (resTack[id]) { return true; } } } resTack[i] = false; return false; }
图的遍历
深度优先搜索
// 递归版本void DFSsearch(Node root){ if (root == null){ return; } visit(root); root.isvisited = true; foreach (Node n in root.adjacent){ search(n); }}//迭代版本,与树的前序遍历类似,先访问本节点的内容,右边入栈,左边入栈void DFSearch(Node root){ if (root == null){ return; } Stack<Node> stack = new Stack(root); stack.push(root); Node temp = null; while(stack.size() > 0){ temp = stack.pop(); if (root.right != null){ stack.push(root.right) } if (root.left != null){ stack.push(root.left); } }}//广度优先的话,就采用队列的形式void search(Node root){ if (null == root){ return; } Quene<Node> que = new LinkedList<Node>(); root.visited = true; visit(root); que.add(root); Node temp = null; while (que.size() > 0){ temp = que.remove(); for (Node n in temp.adjacent){ visit(n); n.visited = true; que.add(n); } }}
注意substring(a, b);从0开始,包含a,但是不包含b。
递归版树的遍历
前序vlr
public void trvelPre(TreeNode root){ if (null == root){ retrun; } visit(root.data); trvelPre(root.left); trvelPre(root.right);}中序lvr以及后续lrv只是修改递归的后三条语句就可以
迭代版的遍历
前序遍历public void trvelPre(TreeNode root){ if (null == root){ return; } Stack<TreeNode> stack = new Stack<TreeNode>(); stack.push(root); while(!stack.isEmpty()){ TreeNode temp = stack.pop(); visit(temp); if (null != temp.right){ stack.push(temp.right); } if (null != temp.left){ stack.push(temp.left); } }}中序遍历,对于完备的树,可以得到parent,空间复杂度为O(1),非完备的就先不这样处理public void trvelIn(TreeNode root){ if (null == root){return;} Stack<TreeNode> stack = new Stack<TreeNode>(); TreeNode temp = root; //中序遍历方式有所不同,碰到左节点就入栈,左节点访问完成后,转向右子节点 while (true){ if (temp != null){ stack.push(temp); temp = temp.left; } else if (!stack.isEmpty()){ temp = stack.pop(); System.out.print(temp.val); temp = temp.right; } else { break; } }}后序遍历public void vistiafter(TreeNode root) { if (null == root ){return;} Stack<TreeNode> stack = new Stack<TreeNode>(); TreeNode temp = root; stack.push(temp); while (!stack.isEmpty()){ if ((stack.peek().left != temp) && (stack.peek().right != temp)){ //当前temp结点既不表示栈顶元素的左节点也不表示右节点, //temp栈顶元素的左兄弟结点或者是root结点 // 表示切换子树 getHLVFL(stack.peek(), stack); } temp = stack.pop(); System.out.print(temp.val); } } public void getHLVFL(TreeNode temp, Stack<TreeNode> stack){ while(null != temp){ if (null != temp.left){ if (null != temp.right){ stack.push(temp.right); } stack.push(temp.left); } else { stack.push(temp.right); } temp = stack.peek(); } stack.pop(); } 后序遍历结束
根据vlr以及lvr,得到lrv,构造出树
public TreeNode getaf(String pre, String in ,int len){ TreeNode node = new TreeNode(); if(len == 0){ return null; } char root = pre.charAt(0); int index = 0; for(int i = 0; i < len; i++){ if(root == in.charAt(i)){ index = i; break; } } node.val = root; node.left = getaf(pre.substring(1,index + 1), in.substring(0, index), index ); node.right = getaf(pre.substring(index + 1, len), in.substring(index + 1, len), len - index - 1); System.out.println(root);//这条语句放的位置,可以输出不同的遍历顺序,现在是后序,最上面是前序,放到中间就是中序 return node; }//root.right = bulidHelp(pre, in, preroot + index - left + 1, index + 1, right);对两边的进行处理
图的DFS深度优先遍历,BFS广度优先遍历,要使用到队列
4.1检查二叉树是否平衡,任意一个结点,其两棵子树的高度差不超过1
思路:递归得到树的高度,递归判断每一个结点是否平衡
public int checkHeight(TreeNode root){ if (null == root){ return 0; } int leftHeight = checkHeight(root.left); if (leftHeight == -1){ return -1; } int rightHeight = cheakHeight(root.right); if (rightHeight == -1){ return -1; } if (Math.abs(leftHeight - rightHeight) > 1){ return -1; } else { return Math.max(leftHeight, rightHeight) + 1; }}public boolean isBlance(TreeNode tree){ if (checkHeight(tree) == -1){ return fasle; } else { return true; }}
4.2给定一个有向图,找出两个节点之间是否存在一条路径
广度优先搜索适合查找最短路径
public enum State{ Unvisited, Visited, Visiting;}public static boolean search(Graph g, Node start, Node end){ if (null == g || null == start){ return false; } LinkedList<Node> list = new LinkedList<Node>(); for (Node u : g.getNodes()){ u.state = State.Unvisited; } list.add(start); start.state = State.Visiting; Node u = null; while(!list.isEmpty()){ u = q.removeFirst(); if (u != null){ for (Node v : u.getAdjacent()){ if (v.state == State.Unvisited){ if (v == end){ return true; } else { v.state = State.Visiting; q.add(v); } } } u.state = State.Visited; } } return false;}
4.3给定一个有序整数数组,元素个不相同,并且按照升序排列,编写算法创建高度最小的二叉查找树。
使处于中间位置上的元素更靠近根节点。
public TreeNode createMin(int nums, int start, int end){ if (start > end){ return null; } int mid = start + ((end - start) >> 1); TreeNode temp = new TreeNode(nums[]); temp.left = createMin(nums, start, mid - 1); temp.right = createMin(nums, start, mid + 1); return temp;}public TreeNode createMinBFS(int[] nums){ return createMin(nums, 0, nums.length - 1);}
4.4给定一个二叉树,创建含有某一深度上的所有结点的链表,比如一棵树的深度为D,创建D个链表。
思路:对广度优先的算法进行稍微修改,一层一层增加即可。
4.5检查一课二叉树是否为二叉查找树
思路:1.如果不存在相等的树,所有左结点小于等于当前结点,当前结点小于右结点,current.left.val<= current.val < current.right.val(程序也可以这么写)。改进,递归
public static int last_printed = integer.MIN_VALUE;public boolean checkBFS(TreeNode root){ if (null == root){return true;} if (!checkBFS(root.left)){ return false; } if (root.val <= last_printed){// 右侧的必须大于 return false; } last_printed = temp.val; if (!checkBFS(root.right)){ return false; } return true;}
思路2,利用最大最小算法,递归,限定当前结点的数的范围,与数学上的夹逼准则类似
public boolean checkMaxMinBFS(TreeNode root, int min, int max){ if (null == root){return true;} if (root.val <= min || root.val > max){ return false; } if (!checkMaxMinBFS(root.left, min, root.val) || !checkMaxMinBFS(root.right, root.val, max)){ return false; } return true;}public boolean checkBFS(TreeNode root){ return checkMaxMinBFS(root, Integer.MIN_VALUE, Integer.MAX_VALUE);}#
4.6找出二叉查找树的下一个结点(可以得到父节点)
思路:也就是中序遍历的下一个结点,此时要分开讨论
public TreeNode leftMostChild(TreeNode root){ if (null == root){return null;} while(root.left != null){ root = root.left; } return root;}public TreeNode inorderSucc(TreeNode n){ if (null == n){return null;} if(n.right != null){ return leftMostChild(n.right); } else { TreeNode cu = n; TreeNode pa = cu.parent; while (pa != null && pa.left != cu){ cu = pa; pa = cu.parent; } return pa; }}
4.7 找出二叉树中两个结点的第一个共同祖先。不得将额外的结点存储在数据结构中
思路:1.从根结点调用cover方法,先检查是否都在根结点中,不在的话返回null,然后依次调用,left与right,
2.递归,优化常数时间 返回值依次为 返回p,root子树包含p不包含q;返回q,root子树中包含q不包含p;返回null,都不包含;否则返回 p与q的共同祖先。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { if(root == null || root == p || root == q) return root; TreeNode left = lowestCommonAncestor(root.left, p, q); TreeNode right = lowestCommonAncestor(root.right, p, q); if(left != null && right != null) return root; return left != null ? left : right; } // 非递归版本的,时间复杂度挺高的。 public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { Map<TreeNode, TreeNode> parents = new HashMap<TreeNode, TreeNode>(); Queue<TreeNode > queue = new LinkedList<>(); parents.put(root, null); queue.offer(root); TreeNode temp = null; while (!parents.containsKey(p) || !parents.containsKey(q)) { temp = queue.poll(); if (temp.left != null) { parents.put(temp.left, temp); queue.offer(temp.left); } if (temp.right != null) { parents.put(temp.right, temp); queue.offer(temp.right); } } Set<TreeNode> anc = new HashSet<>(); while (p != null) { anc.add(p); p = parents.get(p); } while (!anc.contains(q)) { q = parents.get(q); } return q; }
4.8你有一颗非常大的二叉树:T1,有几百万个结点;T2有几百个结点。设计一个算法,判断T2是不是T1的子树
思路1:对两个树前序以及中序遍历,判断T2是不是T1的子串,注意null补全特殊的字符
2:递归调用
public boolean treeMatch(TreeNode a, TreeNode b){ if (a == null && b == null){ return true; } if (a == null || b == null){ return false; } if (a.val != b.val){ return false; } return (treeMatch(a.left, b.left) && treeMatch(a.right, b.right));}public boolean isSubTree(TreeNode t1, TreeNode t2){ if (null == t1 && null == t2){ return true; } if (t1 == null){ return false; } if (t1.val == t2.val){ if (treeMatch(t1, t2)){return true;} } return (isSubTree(t1.left, t2) || isSubTree(t1.right, t2));}
4.9给定一个二叉树,其中每一个结点都含有一个数值。设计一个算法,打印某个结点数值总和等于某个给定值的所有路径。
思路:从根节点出发进行计算
- CC150小结数据结构
- CC150小结概念与算法
- CC150
- Array&String@CC150-基础数据结构实现CloseHash
- 数据结构小结
- 数据结构小结
- 数据结构小结
- 数据结构小结
- 数据结构小结
- 数据结构小结
- 数据结构小结
- [cc150] 3.1
- [cc150] 2.5
- [cc150] 1.4
- [cc150] 1.3
- [cc150] 1.2
- [cc150] 1.1
- cc150-1.1
- UOJ#228——基础数据结构练习题
- 数据库事务隔离级别
- 4个隔离级别
- 第1.3章 node调用dubbo
- POJ 3292 Semi-prime H-numbers (类似素数筛)
- CC150小结数据结构
- 安装 cocoapods 第三方管理工具步骤
- windows环境下 pip 安装 seaborn 失败?
- Centos 7下通过blktrace了解io各阶段性能
- eclipse调用hadoop(出现问题误打误撞)
- IntelliJ IDEA 创建Web项目
- 电影:《三少爷的剑》票房折戟,武侠电影还能翻盘吗?
- java 枚举——java中枚举的运用和使用场景
- JS截取字符串常用方法详细整理