首页 文章 精选 留言 我的

精选列表

搜索[流量结构],共10000篇文章
优秀的个人博客,低调大师

数据结构一:数据+链表 (Datawhale 系列)

Task1.1 数组 1.1.1实现一个支持动态扩容的数组 public class EnsureCapacityArray { private static final Object[] EMPTY_ELEMENTDATA = {}; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; transient Object[] elementData; //传入固定值的情况 public EnsureCapacityArray(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } //没有传入数组大小的情况 public EnsureCapacityArray() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; /** * 扩容 * @param minCapacity */ public void grow(int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); } //数组容量最大的情况 private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; } } 1.1.2 实现一个大小固定的有序数组,支持动态增删改操作 这里理解,大小固定的有序数组,那也就是说数组大小和内部的元素个数完全相同。那么增的时候,需要扩容,删的时候,需要减容。然后数组有序,也就是说,改的时候需要重新排序。 public class FixedArray{ private static final int DEFAULT_SIZE = 10; private static final Object[] EMPTY_ELEMENTDATA = {}; private static final Object[] DEFAULT_ELEMENTDATA = new Object[DEFAULT_SIZE]; transient Object[] elementData; private int size; public FixedArray(){ this.elementData=DEFAULT_ELEMENTDATA; } public FixedArray(int initialCapacity){ if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } //保证增和删时数组长度和元素长度一致 private void ensureCapacityInternal(int minCapacity ){ if (minCapacity - elementData.length > 0) grow(minCapacity); } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; public void grow(int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); } //数组容量最大的情况 private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; } //去掉删除之后,重新排序的,空的数组 public void trimToSize() { if (size < elementData.length) { elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } } //判断数组长度 public int size(){ return size; } //判断数组是否为空 public boolean isEmpty() { return size == 0; } //排序 @SuppressWarnings("unchecked") private <E> void sort() { Arrays.sort((E[]) elementData, 0, size); } //添加 public <E> boolean add(E e) { ensureCapacityInternal(size + 1); elementData[size++] = e; sort(); return true; } //删除 public <E> boolean remove(int index) { if (index >= size) throw new IndexOutOfBoundsException(); System.arraycopy(elementData, index+1, elementData, index, size-1); size--; trimToSize(); return true; } //改 public <E> boolean set(int index,Object e) { elementData[index] = e; sort(); return true; } //查 public Object get(int index) { return elementData[index]; } } 1.1.3 合并两个有序的数组 public static int[] mergeArrays(int[]a ,int[]b){ int alen=a.length; int blen=b.length; int aindex=0; int bindex=0; int [] re=new int[alen+blen]; for(int i=0;i<alen+blen;i++){ if(aindex<alen && bindex<blen){ re[i]=a[aindex] > b[bindex] ? b[bindex++] :a[aindex++]; continue; } if(aindex==alen && bindex<blen ) re[i]=b[bindex++]; if(bindex==blen &&aindex<alen) re[i]=a[aindex++]; } return re; } 1.1.4学习哈希表思想,并完成leetcode上的两数之和(1)及Happy Number(202)! 哈希表 //两数之和 public int[] twoSum(int[] nums, int target) { Map<Integer, Integer> map = new HashMap<>(); for (int i = 0; i < nums.length; i++) { int complement = target - nums[i]; if (map.containsKey(complement)) { return new int[] { map.get(complement), i }; } map.put(nums[i], i); } throw new IllegalArgumentException("No two sum solution"); } //Happy Number(202) class Solution { public boolean isHappy(int n) { if(n==1) return true; while(n!=1 && n!=4){ int a=0; int ans=0; while(n>0){ a=n%10; ans+=a*a; n /=10; } n=ans; } if(n==1) return true; else return false; } } 1.1.5 练习 //Three Sum public List<List<Integer>> threeSum(int[] num) { Arrays.sort(num); List<List<Integer>> res = new LinkedList<>(); for (int i = 0; i < num.length-2; i++) { if (i == 0 || (i > 0 && num[i] != num[i-1])) { int lo = i+1, hi = num.length-1, sum = 0 - num[i]; while (lo < hi) { if (num[lo] + num[hi] == sum) { res.add(Arrays.asList(num[i], num[lo], num[hi])); while (lo < hi && num[lo] == num[lo+1]) lo++; while (lo < hi && num[hi] == num[hi-1]) hi--; lo++; hi--; } else if (num[lo] + num[hi] < sum) lo++; else hi--; } } } return res; } //Majority Element public int majorityElement(int[] nums) { int res=0; int cnt=0; for(int num:nums){ if(cnt==0){ res=num; ++cnt; } else if(num == res) ++cnt; else --cnt; } return res; } //Missing Positive public int firstMissingPositive(int[] nums) { if(nums == null&&nums.length==0) return 1; int i; for(i=0;i<nums.length;i++){ if(nums[i] != i+1){ while(0<nums[i]&& nums[i] <= nums.length && nums[nums[i]-1] != nums[i]){ int temp = nums[i]; nums[i] = nums[nums[i]-1]; nums[temp-1] = temp; } } } for(i=0;i<nums.length;i++) { if(nums[i]!=i+1) { return i+1; } } return i+1; } TASK1.2 链表 1.2.1 实现单链表、循环链表、双向链表,支持增删操作 //单链表 class singleNode{ public int data; public singleNode next; public singleNode head =null; public singleNode(int data){ this.data=data; } public void addNode(singleNode node){ singleNode newNode = new singleNode(data); if(head == null){ head = newNode; return; } singleNode temp = head; while(temp.next != null){ temp = temp.next; } temp.next = newNode; } public boolean removeNode(int index){ if(index<1 || index>length()){ return false; } if(index == 1){//删除头结点 head = head.next; return true; } singleNode preNode = head; singleNode curNode = preNode.next; int i = 1; while(curNode != null){ if(i==index){//寻找到待删除结点 preNode.next = curNode.next;//待删除结点的前结点指向待删除结点的后结点 return true; } //当先结点和前结点同时向后移 preNode = preNode.next; curNode = curNode.next; i++; } return true; } public int length(){ int length = 0; singleNode curNode = head; while(curNode != null){ length++; curNode = curNode.next; } return length; } } //双向链表 //单向循环链表类 public class CycleLinkList implements List { Node head; //头指针 Node current;//当前结点对象 int size;//结点个数 //初始化一个空链表 public CycleLinkList() { //初始化头结点,让头指针指向头结点。并且让当前结点对象等于头结点。 this.head = current = new Node(null); this.size =0;//单向链表,初始长度为零。 this.head.next = this.head; } //定位函数,实现当前操作对象的前一个结点,也就是让当前结点对象定位到要操作结点的前一个结点。 //比如我们要在a2这个节点之前进行插入操作,那就先要把当前节点对象定位到a1这个节点,然后修改a1节点的指针域 public void index(int index) throws Exception { if(index <-1 || index > size -1) { throw new Exception("参数错误!"); } //说明在头结点之后操作。 if(index==-1) //因为第一个数据元素结点的下标是0,那么头结点的下标自然就是-1了。 return; current = head.next; int j=0;//循环变量 while(current != head&&j<index) { current = current.next; j++; } } @Override public void delete(int index) throws Exception { // TODO Auto-generated method stub //判断链表是否为空 if(isEmpty()) { throw new Exception("链表为空,无法删除!"); } if(index <0 ||index >size) { throw new Exception("参数错误!"); } index(index-1);//定位到要操作结点的前一个结点对象。 current.setNext(current.next.next); size--; } @Override public Object get(int index) throws Exception { // TODO Auto-generated method stub if(index <-1 || index >size-1) { throw new Exception("参数非法!"); } index(index); return current.getElement(); } @Override public void insert(int index, Object obj) throws Exception { // TODO Auto-generated method stub if(index <0 ||index >size) { throw new Exception("参数错误!"); } index(index-1);//定位到要操作结点的前一个结点对象。 current.setNext(new Node(obj,current.next)); size++; } @Override public boolean isEmpty() { // TODO Auto-generated method stub return size==0; } @Override public int size() { // TODO Auto-generated method stub return this.size; } } //双向循环链表 //单向链表类 public class DoubleCycleLinkList implements List { Node head; //头指针 Node current;//当前结点对象 int size;//结点个数 //初始化一个空链表 public DoubleCycleLinkList() { //初始化头结点,让头指针指向头结点。并且让当前结点对象等于头结点。 this.head = current = new Node(null); this.size = 0;//单向链表,初始长度为零。 this.head.next = head; this.head.prior = head; } //定位函数,实现当前操作对象的前一个结点,也就是让当前结点对象定位到要操作结点的前一个结点。 public void index(int index) throws Exception { if (index < -1 || index > size - 1) { throw new Exception("参数错误!"); } //说明在头结点之后操作。 if (index == -1) return; current = head.next; int j = 0;//循环变量 while (current != head && j < index) { current = current.next; j++; } } @Override public void delete(int index) throws Exception { // TODO Auto-generated method stub //判断链表是否为空 if (isEmpty()) { throw new Exception("链表为空,无法删除!"); } if (index < 0 || index > size) { throw new Exception("参数错误!"); } index(index - 1);//定位到要操作结点的前一个结点对象。 current.setNext(current.next.next); current.next.setPrior(current); size--; } @Override public Object get(int index) throws Exception { // TODO Auto-generated method stub if (index < -1 || index > size - 1) { throw new Exception("参数非法!"); } index(index); return current.getElement(); } @Override public void insert(int index, Object obj) throws Exception { // TODO Auto-generated method stub if (index < 0 || index > size) { throw new Exception("参数错误!"); } index(index - 1);//定位到要操作结点的前一个结点对象。 current.setNext(new Node(obj, current.next)); current.next.setPrior(current); current.next.next.setPrior(current.next); size++; } @Override public boolean isEmpty() { // TODO Auto-generated method stub return size == 0; } @Override public int size() { // TODO Auto-generated method stub return this.size; } } 1.2.2 实现单链表反转 public ListNode reverseList(ListNode head) { if (head == null || head.next == null) return head; ListNode p = reverseList(head.next); head.next.next = head; head.next = null; return p; } 1.2.3 实现两个有序的链表合并为一个有序链表 ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { if(l1 == NULL) return l2; if(l2 == NULL) return l1; if(l1->val < l2->val) { l1->next = mergeTwoLists(l1->next, l2); return l1; } else { l2->next = mergeTwoLists(l2->next, l1); return l2; } } 1.2.4 实现求链表的中间结点 public ListNode middleNode(ListNode head) { ListNode fast=head; ListNode low=head; while(fast != null && fast.next != null){ low = low.next; fast= fast.next.next; } return low; } 1.2.5 练习 //Linked List Cycle I(环形链表) public boolean hasCycle(ListNode head) { ListNode f=head; ListNode s=head; if(head == null || head.next == null) return false; while(f!=null && f.next!=null){ f=f.next.next; s=s.next; if(f == s) return true; } return false; } //Merge k Sorted Lists(合并 k 个排序链表) public ListNode mergeKLists(ListNode[] lists){ if(lists.length == 0) return null; if(lists.length == 1) return lists[0]; if(lists.length == 2){ return mergeTwoLists(lists[0],lists[1]); } int mid = lists.length/2; ListNode[] l1 = new ListNode[mid]; for(int i = 0; i < mid; i++){ l1[i] = lists[i]; } ListNode[] l2 = new ListNode[lists.length-mid]; for(int i = mid,j=0; i < lists.length; i++,j++){ l2[j] = lists[i]; } return mergeTwoLists(mergeKLists(l1),mergeKLists(l2)); } public ListNode mergeTwoLists(ListNode l1, ListNode l2) { if (l1 == null) return l2; if (l2 == null) return l1; ListNode head = null; if (l1.val <= l2.val){ head = l1; head.next = mergeTwoLists(l1.next, l2); } else { head = l2; head.next = mergeTwoLists(l1, l2.next); } return head; }

优秀的个人博客,低调大师

Python 数据结构与算法 —— 初识算法

算法是什么? 举个简单例子: 我们要做一份蛋炒饭: 拿钱包,出门,去菜市场购买鸡蛋和大米以及油和盐——购买蛋炒饭的材料 回家将大米淘洗干净放进电饭煲——煮熟大米 将锅放在电磁炉上加热——往锅里倒适量油 将鸡蛋打开放入油锅——翻炒鸡蛋至七分熟 将适量煮熟的米饭倒入锅中,加盐——翻炒两分钟 以上就是制作一份简单蛋炒饭的步骤 如果把这些交给机器来做,也是如此,并且步骤将更加细分和严谨 简单来讲,这就是算法 那么算法到底是什么呢? 先来看一道简单的高中数学题: 现有a,b,c三个自然数,要求满足以下条件: 1.a+b+c = 1000 2.a^2 + b^2 = c^2 (^代表平方) 分析: 首先排除数学公式,我们使用机器思维来计算这道题,能想到的办法也很简单,即一个一个数尝试 ,直到试出准确答案为止,此种方法我们称之为 枚举法 上面数学题使用Python来实现: import time start_time = time.time() for a in range(0, 1001): for b in range(0, 1001): for c in range(0, 1001): if a+b+c == 1000 and a**2 + b**2 == c**2: print("a, b, c: %d, %d,%d" % (a, b, c)) end_time = time.time() print("time:%d" % (end_time - start_time)) print("finished!") 代码执行结果: C:\python3\setup\python.exe C:/Users/limia/Desktop/DataS/01_枚举组合.py a, b, c: 0, 500,500 a, b, c: 200, 375,425 a, b, c: 375, 200,425 a, b, c: 500, 0,500 time:121 finished! Process finished with exit code 0 注释: 在上面的代码中:计算这道数学题的同时,还引入了time模块来计算这段代码的运行时间,以方便之后对比算法效率 通过分析这道题,我们可以得知,a+b+c=1000,在有了a和b的值之后,c的值自然就可以计算为:1000-a-b,分析至此,则代码可以改进为以下: import time start_time = time.time() for a in range(0, 1001): for b in range(0, 1001): c = 1000 - a -b if a**2 + b**2 == c**2: print("a, b, c: %d, %d,%d" % (a, b, c)) end_time = time.time() print("time:%d" % (end_time - start_time)) print("finished!") 代码执行结果: C:\python3\setup\python.exe C:/Users/limia/Desktop/DataS/01_枚举组合.py a, b, c: 0, 500,500 a, b, c: 200, 375,425 a, b, c: 375, 200,425 a, b, c: 500, 0,500 time:1 finished! Process finished with exit code 0 通过对比,可以一目了然的发现,改进后的代码执行效率(1s)明显高于第一种代码执行效率(121s) 算法的概念: 算法是计算机处理信息的本质 算法是独立存在的一种解决问题的方法和思想 计算机程序本质上是一个算法来告诉计算机确切的步骤来执行一个指定的任务 单纯以时间衡量算法效率是否是科学的、客观的? 答案:不客观 假设在一台古老的计算机上运行上面的两个程序,则其所消耗的时间都将是极长的,因此单纯以时间计算算法的效率是不科学的。 在衡量算法的效率时,应当脱离计算机来估算 因此引入时间复杂度来衡量算法的效率 每台计算机执行的总时间不同,但是执行基本运算数量大体相同 上面的两个程序,以时间复杂度来表示算法效率: T = 1000 * 1000 * 1000 * 2 ==》 当计算的不是a+b+c=1000,而是a+b+c=2000时,以时间复杂度表示则 T = 2000 * 2000 * 2000 * 2 ==》 当计算的不是1000或者2000,而是n呢? T(n) = n * n * n * 2 简化: T(n) = n^3 * 2 则此时 **T(n) = n^3 * 2 ** 即为这个程序算法的时间复杂度函数 通过函数 T(n) = n^3 * 2 ,可做出曲线图 系数对曲线形状改变不大,只是陡峭不同,因此 T(n) = n^3 * 2 可以简化为T(n) = n^3 T(n) = n^3 则可以叫做 T(n) = n^3 * 2 的渐进函数 大O表示法 上述 **T(n) = n^3 ** 就是 ** T(n) = n^3 * 2 ** 的大O 表示法 总结: 大O表示法只留下表示特征的部分 常见时间复杂度之间的关系 O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n) 个人博客地址:www.limiao.tech 微信公众号:TechBoard 慕课网:techLee 如果觉得文章对您有所帮助,那就伸出小手 ==>> 点击下方 [ like ] 吧

优秀的个人博客,低调大师

java数据结构题之约瑟夫问题

约瑟夫问题:转载自约瑟夫问题 据说着名犹太历史/数学家约瑟夫(Josephus)有过以下的故事:在罗马人占领乔塔帕特後,40个犹太士兵与约瑟夫躲到一个洞中,眼见脱逃无望,一群人决定集体自杀,约瑟夫建议自杀方式,41个人排成圆圈,由第1个人开始报数,每报数到5的人就必须自杀,然後由下一个重新报数,直到所有人都自杀身亡为止。如果你是约瑟夫,你应该在哪个位置才能活下来(最后只剩下你)? 我的答案: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package p1; import java.util.LinkedList; import java.util.List; public class KillSelf{ //构造链式列表,用来模拟人。 private static List<String>list= new LinkedList<String>(); //记忆自杀那个人前后的人集合有序子列表 private static List<String>listBefore,listAfter; //自杀那个人的编号从1开始 private static StringkilledNum= null ; private static int KILL_INDEX= 4 ; //记录自杀的总人数 private static int sum= 0 ; public static void main(String[]args){ //记住每个从最开始的编号 for ( int i= 1 ;i<= 41 ;i++) { list.add(i+ "" ); } //其实自杀的过程,是一个循环的过程,所以用循环来解决。 while ( true ) { //获取自杀位置前后的子集 if (list.size()>= 5 ) //当人数大于等于5个人时 { listBefore= new LinkedList<String>(list.subList( 0 ,KILL_INDEX)); //不能直接用subList的返回值,要包装一下 listAfter= new LinkedList<String>(list.subList(KILL_INDEX+ 1 ,list.size())); } else if (list.size()> 1 &&list.size()< 5 ) //当人数多于1个人但是少于5个人时 { KILL_INDEX= 5 %list.size()- 1 ; //这个判断很巧妙 if (KILL_INDEX> 0 &&KILL_INDEX<list.size()- 1 ) { listBefore= new LinkedList<String>(list.subList( 0 ,KILL_INDEX)); //不能直接用subList的返回值,要包装一下 listAfter= new LinkedList<String>(list.subList(KILL_INDEX+ 1 ,list.size())); } else if (KILL_INDEX== 0 ) { listBefore.clear(); listAfter= new LinkedList<String>(list.subList(KILL_INDEX+ 1 ,list.size())); } else if (KILL_INDEX==list.size()- 1 ) { listBefore= new LinkedList<String>(list.subList( 0 ,KILL_INDEX)); listAfter.clear(); } } else break ; //将子集的后与前连接起来,更新总的集合 killedNum=list.get(KILL_INDEX); sum++; System.out.println( "编号" +killedNum+ "已自杀!-----自杀总人数达" +sum); //更新list list.clear(); list.addAll(listAfter); list.addAll(listBefore); System.out.println( "剩余人员编号:" +list); System.out.println( "" ); } System.out.println( "" ); System.out.println( "结论:处在" +list.get( 0 )+ "号才不会自杀" ); } } 但是网上的帖子,几行代码就解决问题了,这就是算法的魅力! 本文转自屠夫章哥 51CTO博客,原文链接:http://blog.51cto.com/4259297/1658382,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册