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给定一个二叉树,其中每一个结点都含有一个数值。设计一个算法,打印某个结点数值总和等于某个给定值的所有路径。

思路:从根节点出发进行计算

1 0
原创粉丝点击