`
wmj2003
  • 浏览: 99745 次
  • 来自: ...
文章分类
社区版块
存档分类
最新评论

递归排序的一种实现方法。

阅读更多
  1. importjava.util.Comparator;
  2. importcom.work.qxgl.model.QxglDept;
  3. /**
  4. *@authorwangmingjie
  5. *@date2008-9-5上午10:33:59
  6. */
  7. publicclassQxglDeptCompartorimplementsComparator<QxglDept>{
  8. publicintcompare(QxglDepto1,QxglDepto2){
  9. returno1.getDeptIntroduce().compareTo(o2.getDeptIntroduce());
  10. }
  11. }
  1. importjava.util.LinkedList;
  2. importjava.util.List;
  3. importjava.util.Arrays;
  4. importcom.work.qxgl.model.QxglDept;
  5. /**
  6. *递归排序(测试成功!)
  7. *前提:知道了父节点,知道了节点所在的级别,级别内部的排序号是整数且都是唯一的。
  8. *@authorwangmingjie
  9. *@date2008-9-4下午10:05:17
  10. */
  11. publicclassSelectTreeTwo{
  12. //崔的算法:首先,增加一个属性用来组成排序的字符串的,将父结构的排序id组成字符串,规定从第1级开始排序号要定长。(最顶级为0级)
  13. //然后将组合后的排序字符串排序,递归排序就实现了。
  14. //这种算法的同一级别内排序号必须不一样才可以,否则排序将失败!
  15. publicstaticList<QxglDept>init(){
  16. List<QxglDept>l=newLinkedList<QxglDept>();
  17. //QxglDeptid,名称,父id,级别,排序号(deptIntroduce是辅助字段)
  18. QxglDepttemp=newQxglDept("0","中国",null,0,0);
  19. l.add(temp);
  20. //=====================第一级==========================
  21. temp=newQxglDept("2","山东省","0",1,2);
  22. l.add(temp);
  23. temp=newQxglDept("3","河北省","0",1,3);
  24. l.add(temp);
  25. temp=newQxglDept("1","北京市","0",1,1);
  26. l.add(temp);
  27. //0-1-11
  28. //0-10-1
  29. //0-9-1
  30. //=====================第二级==========================
  31. temp=newQxglDept("11","市辖区","1",2,11);
  32. l.add(temp);//,"0-1-11"
  33. temp=newQxglDept("12","县","1",2,12);
  34. l.add(temp);//,"0-1-12"
  35. temp=newQxglDept("21","济南市","2",2,1);
  36. l.add(temp);
  37. temp=newQxglDept("23","潍坊市","2",2,3);
  38. l.add(temp);
  39. temp=newQxglDept("22","青岛市","2",2,2);
  40. l.add(temp);
  41. temp=newQxglDept("32","唐山市","3",2,32);
  42. l.add(temp);
  43. temp=newQxglDept("31","石家庄","3",2,31);
  44. l.add(temp);
  45. //=======================第三级=================================
  46. temp=newQxglDept("112","朝阳区","11",3,112);
  47. l.add(temp);
  48. temp=newQxglDept("111","东城区","11",3,111);
  49. l.add(temp);
  50. temp=newQxglDept("113","西城区","11",3,113);
  51. l.add(temp);
  52. temp=newQxglDept("122","延庆县","12",3,122);
  53. l.add(temp);
  54. temp=newQxglDept("121","密云县","12",3,121);
  55. l.add(temp);
  56. temp=newQxglDept("213","天桥区","21",3,213);
  57. l.add(temp);
  58. temp=newQxglDept("212","市中区","21",3,212);
  59. l.add(temp);
  60. temp=newQxglDept("211","历下区","21",3,211);
  61. l.add(temp);
  62. returnl;
  63. }
  64. /**
  65. *入口方法main
  66. *@paramargs
  67. */
  68. publicstaticvoidmain(String[]args){
  69. longstart=System.currentTimeMillis();
  70. LinkedList<QxglDept>l=(LinkedList<QxglDept>)SelectTreeTwo.init();//
  71. intLEN=l.size();
  72. System.out.println("目标list大小为:"+LEN);
  73. for(inti=0;i<LEN;i++){
  74. setOrderString(l,l.get(i),i);
  75. }
  76. sortList(l);
  77. printTree(l);
  78. System.out.println("总共花费"+(System.currentTimeMillis()-start)+"毫秒");
  79. }
  80. /**
  81. *根据DeptIntroduce,由小到大将list排序
  82. *
  83. *@paraml
  84. */
  85. publicstaticvoidsortList(LinkedList<QxglDept>l){
  86. //首先转换成数组
  87. QxglDept[]dest=newQxglDept[l.size()];
  88. dest=(QxglDept[])l.toArray(dest);
  89. Arrays.sort(dest,newQxglDeptCompartor());
  90. intLEN=l.size();
  91. l.clear();//必须先清空,然后按照数组的顺序增加
  92. for(inti=0;i<LEN;i++){
  93. l.add(i,dest[i]);
  94. }
  95. }
  96. /**
  97. *找到dept的父节点
  98. *
  99. *@paraml
  100. *@paramdept
  101. *@return
  102. */
  103. publicstaticintgetParentIndex(LinkedList<QxglDept>l,QxglDeptdept){
  104. intLEN=l.size();
  105. intresult=-1;
  106. StringparentId=dept.getDeptParentId();
  107. for(inti=0;i<LEN;i++){
  108. if(parentId.equals(l.get(i).getId())){
  109. result=i;
  110. break;
  111. }else{
  112. continue;
  113. }
  114. }
  115. returnresult;
  116. }
  117. /**
  118. *生成排序号的字符串
  119. *
  120. *@paraml
  121. *@paramdept
  122. *@parampos
  123. */
  124. publicstaticvoidsetOrderString(LinkedList<QxglDept>l,QxglDeptdept,
  125. intpos){
  126. if(pos==0){
  127. dept.setDeptIntroduce(dept.getDeptOrderId()+"");
  128. }else{
  129. //首先找到父节点,获取到他的DeptIntroduce,对自己当前的排序号补零;
  130. QxglDeptparentNode=l.get(getParentIndex(l,dept));
  131. dept.setDeptIntroduce(parentNode.getDeptIntroduce()+"-"
  132. +getOrderString(dept.getDeptOrderId()));
  133. }
  134. }
  135. /**
  136. *2^31=2147483648,最大为10位
  137. *
  138. *@paramorderId
  139. *@return
  140. */
  141. publicstaticStringgetOrderString(intorderId){
  142. Stringtemp="";
  143. intLEN=(orderId+"").length();
  144. for(inti=0;i<10-LEN;i++){
  145. temp=temp+"0";
  146. }
  147. temp=temp+orderId;
  148. returntemp;
  149. }
  150. publicstaticvoidprint(List<QxglDept>l){
  151. for(inti=0;i<l.size();i++){
  152. QxglDepttemp=l.get(i);
  153. System.out.println(temp.getDeptName()+"||"
  154. +temp.getDeptIntroduce());
  155. }
  156. }
  157. /**
  158. *打印树状菜单
  159. *@paraml
  160. */
  161. publicstaticvoidprintTree(List<QxglDept>l){
  162. for(inti=0;i<l.size();i++){
  163. QxglDepttemp=l.get(i);
  164. if(temp.getDeptLevel()==0)
  165. System.out.println(l.get(i).getDeptName());
  166. else{
  167. for(intj=0;j<temp.getDeptLevel()-1;j++){
  168. System.out.print(" ");//补齐空格,在html中最好使用全角空格(汉字空格)。
  169. }
  170. System.out.println("┣"+l.get(i).getDeptName());
  171. }
  172. }
  173. }
  174. }

需要的pojo:

  1. publicclassQxglDeptimplementsSerializable{
  2. /**
  3. *
  4. */
  5. privatestaticfinallongserialVersionUID=-1536071285282466850L;
  6. //constructors
  7. publicQxglDept(){
  8. initialize();
  9. }
  10. /**
  11. *Constructorforprimarykey
  12. */
  13. publicQxglDept(java.lang.Stringid){
  14. this.setId(id);
  15. initialize();
  16. }
  17. /**
  18. *Constructorforrequiredfields
  19. */
  20. publicQxglDept(java.lang.Stringid,java.lang.StringdeptName,
  21. java.lang.StringdeptParentId,intdeptLevel,
  22. intdeptOrderId){
  23. this.setId(id);
  24. this.setDeptName(deptName);
  25. this.setDeptParentId(deptParentId);
  26. this.setDeptLevel(deptLevel);
  27. this.setDeptOrderId(deptOrderId);
  28. initialize();
  29. }
  30. protectedvoidinitialize(){
  31. }
  32. privateinthashCode=Integer.MIN_VALUE;
  33. //primarykey
  34. privatejava.lang.Stringid;
  35. //fields
  36. privatejava.lang.StringdeptName;
  37. privatejava.lang.StringdeptParentId;//父部门的id
  38. privateintdeptLevel;//部门级别
  39. privatejava.lang.StringdeptIdPath;//id全路径
  40. privatejava.lang.StringdeptFullname;//部门全称
  41. privateintdeptOrderId;//排序id,要能够调整
  42. //privateStringdeptCreateTime;//创建时间,由数据库自己来维护
  43. //collections
  44. privateStringdeptArea;//所在地区
  45. privateStringdeptType;//部门类别
  46. privateStringdeptLinkman;//联系人
  47. privateStringdeptLinkmanphone;//联系人电话
  48. privateStringdeptEmail;//部门电子邮箱
  49. privateStringdeptPhone;//部门电话
  50. privateStringdeptFax;//部门传真
  51. privateStringdeptAddress;//部门地址
  52. privateStringdeptPostalcode;//部门邮编
  53. privateStringdeptIntroduce;//部门简介
  54. /**
  55. *@returnthedeptAddress
  56. */
  57. publicStringgetDeptAddress(){
  58. returndeptAddress;
  59. }
  60. /**
  61. *@paramdeptAddress
  62. *thedeptAddresstoset
  63. */
  64. publicvoidsetDeptAddress(StringdeptAddress){
  65. this.deptAddress=deptAddress;
  66. }
  67. /**
  68. *@returnthedeptEmail
  69. */
  70. publicStringgetDeptEmail(){
  71. returndeptEmail;
  72. }
  73. /**
  74. *@paramdeptEmail
  75. *thedeptEmailtoset
  76. */
  77. publicvoidsetDeptEmail(StringdeptEmail){
  78. this.deptEmail=deptEmail;
  79. }
  80. /**
  81. *@returnthedeptFax
  82. */
  83. publicStringgetDeptFax(){
  84. returndeptFax;
  85. }
  86. /**
  87. *@paramdeptFax
  88. *thedeptFaxtoset
  89. */
  90. publicvoidsetDeptFax(StringdeptFax){
  91. this.deptFax=deptFax;
  92. }
  93. /**
  94. *@returnthedeptLinkman
  95. */
  96. publicStringgetDeptLinkman(){
  97. returndeptLinkman;
  98. }
  99. /**
  100. *@paramdeptLinkman
  101. *thedeptLinkmantoset
  102. */
  103. publicvoidsetDeptLinkman(StringdeptLinkman){
  104. this.deptLinkman=deptLinkman;
  105. }
  106. /**
  107. *@returnthedeptLinkmanphone
  108. */
  109. publicStringgetDeptLinkmanphone(){
  110. returndeptLinkmanphone;
  111. }
  112. /**
  113. *@paramdeptLinkmanphone
  114. *thedeptLinkmanphonetoset
  115. */
  116. publicvoidsetDeptLinkmanphone(StringdeptLinkmanphone){
  117. this.deptLinkmanphone=deptLinkmanphone;
  118. }
  119. /**
  120. *@returnthedeptPhone
  121. */
  122. publicStringgetDeptPhone(){
  123. returndeptPhone;
  124. }
  125. /**
  126. *@paramdeptPhone
  127. *thedeptPhonetoset
  128. */
  129. publicvoidsetDeptPhone(StringdeptPhone){
  130. this.deptPhone=deptPhone;
  131. }
  132. /**
  133. *@returnthedeptPostalcode
  134. */
  135. publicStringgetDeptPostalcode(){
  136. returndeptPostalcode;
  137. }
  138. /**
  139. *@paramdeptPostalcode
  140. *thedeptPostalcodetoset
  141. */
  142. publicvoidsetDeptPostalcode(StringdeptPostalcode){
  143. this.deptPostalcode=deptPostalcode;
  144. }
  145. /**
  146. *@returnthedeptType
  147. */
  148. publicStringgetDeptType(){
  149. returndeptType;
  150. }
  151. /**
  152. *@paramdeptType
  153. *thedeptTypetoset
  154. */
  155. publicvoidsetDeptType(StringdeptType){
  156. this.deptType=deptType;
  157. }
  158. /**
  159. *Returntheuniqueidentifierofthisclass
  160. *
  161. *@hibernate.idgenerator-class="uuid"column="dept_id"
  162. */
  163. publicjava.lang.StringgetId(){
  164. returnid;
  165. }
  166. /**
  167. *Settheuniqueidentifierofthisclass
  168. *
  169. *@paramid
  170. *thenewID
  171. */
  172. publicvoidsetId(java.lang.Stringid){
  173. this.id=id;
  174. this.hashCode=Integer.MIN_VALUE;
  175. }
  176. /**
  177. *Returnthevalueassociatedwiththecolumn:dept_name
  178. */
  179. publicjava.lang.StringgetDeptName(){
  180. returndeptName;
  181. }
  182. /**
  183. *Setthevaluerelatedtothecolumn:dept_name
  184. *
  185. *@paramdeptName
  186. *thedept_namevalue
  187. */
  188. publicvoidsetDeptName(java.lang.StringdeptName){
  189. this.deptName=deptName;
  190. }
  191. /**
  192. *Returnthevalueassociatedwiththecolumn:dept_parent_id
  193. */
  194. publicjava.lang.StringgetDeptParentId(){
  195. returndeptParentId;
  196. }
  197. /**
  198. *Setthevaluerelatedtothecolumn:dept_parent_id
  199. *
  200. *@paramdeptParentId
  201. *thedept_parent_idvalue
  202. */
  203. publicvoidsetDeptParentId(java.lang.StringdeptParentId){
  204. this.deptParentId=deptParentId;
  205. }
  206. /**
  207. *Returnthevalueassociatedwiththecolumn:dept_level
  208. */
  209. publicintgetDeptLevel(){
  210. returndeptLevel;
  211. }
  212. /**
  213. *Setthevaluerelatedtothecolumn:dept_level
  214. *
  215. *@paramdeptLevel
  216. *thedept_levelvalue
  217. */
  218. publicvoidsetDeptLevel(intdeptLevel){
  219. this.deptLevel=deptLevel;
  220. }
  221. /**
  222. *Returnthevalueassociatedwiththecolumn:dept_id_path
  223. */
  224. publicjava.lang.StringgetDeptIdPath(){
  225. returndeptIdPath;
  226. }
  227. /**
  228. *Setthevaluerelatedtothecolumn:dept_id_path
  229. *
  230. *@paramdeptIdPath
  231. *thedept_id_pathvalue
  232. */
  233. publicvoidsetDeptIdPath(java.lang.StringdeptIdPath){
  234. this.deptIdPath=deptIdPath;
  235. }
  236. /**
  237. *Returnthevalueassociatedwiththecolumn:dept_fullname
  238. */
  239. publicjava.lang.StringgetDeptFullname(){
  240. returndeptFullname;
  241. }
  242. /**
  243. *Setthevaluerelatedtothecolumn:dept_fullname
  244. *
  245. *@paramdeptFullname
  246. *thedept_fullnamevalue
  247. */
  248. publicvoidsetDeptFullname(java.lang.StringdeptFullname){
  249. this.deptFullname=deptFullname;
  250. }
  251. publicbooleanequals(Objectobj){
  252. if(null==obj)
  253. returnfalse;
  254. if(!(objinstanceofQxglDept))
  255. returnfalse;
  256. else{
  257. QxglDeptqxglDept=(QxglDept)obj;
  258. if(null==this.getId()||null==qxglDept.getId())
  259. returnfalse;
  260. else
  261. return(this.getId().equals(qxglDept.getId()));
  262. }
  263. }
  264. publicinthashCode(){
  265. if(Integer.MIN_VALUE==this.hashCode){
  266. if(null==this.getId())
  267. returnsuper.hashCode();
  268. else{
  269. StringhashStr=this.getClass().getName()+":"
  270. +this.getId().hashCode();
  271. this.hashCode=hashStr.hashCode();
  272. }
  273. }
  274. returnthis.hashCode;
  275. }
  276. publicStringtoString(){
  277. return"QxglDept{dept_id="+id+",dept_name="+deptName
  278. +",dept_Parent_Id="+deptParentId+",dept_leve="+deptLevel
  279. +",deptIdPath="+deptIdPath+",dept_fullname="
  280. +deptFullname+",dept_order_id="+deptOrderId+",deptArea="
  281. +deptArea+","+"dept_type="+deptType+"}";
  282. }
  283. //publicStringgetDeptCreateTime(){
  284. //returndeptCreateTime;
  285. //}
  286. //
  287. //publicvoidsetDeptCreateTime(StringdeptCreateTime){
  288. //this.deptCreateTime=deptCreateTime;
  289. //}
  290. publicintgetDeptOrderId(){
  291. returndeptOrderId;
  292. }
  293. publicvoidsetDeptOrderId(intdeptOrderId){
  294. this.deptOrderId=deptOrderId;
  295. }
  296. publicintgetHashCode(){
  297. returnhashCode;
  298. }
  299. publicvoidsetHashCode(inthashCode){
  300. this.hashCode=hashCode;
  301. }
  302. /**
  303. *@returnthedeptArea
  304. */
  305. publicStringgetDeptArea(){
  306. returndeptArea;
  307. }
  308. /**
  309. *@paramdeptArea
  310. *thedeptAreatoset
  311. */
  312. publicvoidsetDeptArea(StringdeptArea){
  313. this.deptArea=deptArea;
  314. }
  315. /**
  316. *@returnthedeptIntroduce
  317. */
  318. publicStringgetDeptIntroduce(){
  319. returndeptIntroduce;
  320. }
  321. /**
  322. *@paramdeptIntroduce
  323. *thedeptIntroducetoset
  324. */
  325. publicvoidsetDeptIntroduce(StringdeptIntroduce){
  326. this.deptIntroduce=deptIntroduce;
  327. }
  328. }

目标list大小为:19
中国
┣北京市
 ┣市辖区
  ┣东城区
  ┣朝阳区
  ┣西城区
 ┣县
  ┣密云县
  ┣延庆县
┣山东省
 ┣济南市
  ┣历下区
  ┣市中区
  ┣天桥区
 ┣青岛市
 ┣潍坊市
┣河北省
 ┣石家庄
 ┣唐山市
总共花费15毫秒


分享到:
评论

相关推荐

    递归函数 递归排序法

    递归排序法是利用递归函数实现排序的一种方法。最著名的递归排序算法之一是快速排序(Quick Sort)。快速排序的核心思想是分治法:选取一个基准元素,将数组分为小于基准的元素和大于或等于基准的元素两部分,然后对...

    快速排序 --- 非递归实现

    快速排序是一种高效的排序算法,由英国计算机科学家C.A.R. Hoare在1960年提出。它的基本思想是分治法,通过一趟排序将待排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据都要小,然后再按...

    递归方法实现快速排序

    3. **递归排序**:对基准左边的子数组和右边的子数组分别进行快速排序,这个过程通过递归调用快速排序函数来实现。如果子数组为空,递归结束;否则,重复步骤1和2。 4. **合并结果**:由于排序是就地进行的,不需要...

    堆排序递归和非递归的实现

    总的来说,这个资源提供了一个学习和实践堆排序算法的机会,涵盖了递归和非递归两种实现方式,对于理解和掌握堆排序算法有着重要的帮助。在实际应用中,根据具体场景和性能需求,可以选择适合的实现方式。

    快速排序的非递归实现

    总的来说,非递归快速排序是一种利用栈来实现的高效排序方法,它通过模拟递归过程,避免了递归调用的开销,适用于处理大规模数据。理解并熟练掌握这种算法,对于提升编程能力尤其是算法设计与分析能力非常有帮助。

    快速选择非递归与递归算法实现

    快速选择算法是基于快速排序的一种高效选择算法,用于在未排序的数据集合中找到第k小(或第k大)的元素。它由C.A.R. Hoare在1960年提出,是线性时间复杂度的算法,在平均情况下表现优秀。在本主题中,我们将深入探讨...

    快速排序的递归简洁实现

    快速排序是一种高效的排序算法,由英国计算机科学家托尼·霍尔于1960年提出。它采用分治策略来把一个序列分为较小的两个子序列,然后递归地对子序列进行排序,最终合并得到有序序列。快速排序在平均情况下的时间...

    matlab递归排序实现.zip_matlab_递归排序

    递归是一种解决问题的方法,它通过调用自身来解决更小规模的相同问题,直到达到基本情况,从而解决整个问题。在排序算法中,递归常用于实现分治策略,如快速排序、归并排序等。 归并排序(Merge Sort)是基于分治...

    c语言学习之排序 数据结构 链表 堆排序 希尔排序 快速排序 递归排序

    递归排序是一种基于递归的排序算法,时间复杂度为O(nlogn)。递归排序的原理是将数组分成两个部分,一部分是已经排好序的,一部分是未排序的。通过递归地将这两个部分排序,直到整个数组都排好序。 代码实现 下面是...

    冒泡排序和递归求和实现

    递归是一种解决问题的方法,它解决问题的各个部分,而这些部分又是相同问题的规模较小的实例。在计算求和问题中,递归通常涉及到将总和分为两个部分:一部分是第一个数字,另一部分是剩余数字的总和。递归求和的基线...

    非递归的归并排序(一种优排序)

    归并排序是一种高效的排序算法,基于分治策略。在计算机科学中,分治法是一种将大问题分解成小问题来解决的策略。归并排序利用这一策略,将一个大数组分成两个小数组,分别对它们进行排序,然后将排好序的小数组合并...

    利用递归算法实现的基数排序算法

    递归算法则是一种解决问题的方法,它解决问题的每一个子问题都是原问题的缩小版,直到子问题简单到可以直接求解。在基数排序中,递归通常用于处理每一位的排序,将大数转换为小数的过程。 基数排序的基本步骤如下:...

    归并排序的非递归实现

    归并排序是一种常用的排序算法,它的基本思想是将需要排序的数组分成两个或多个小数组,分别对每个小数组进行排序,然后将已排序的小数组合并成一个大的已排序数组。这种算法的时间复杂度为O(n log n)。 二、非递归...

    java 快速排序 折半查找的界面实现 (递归与分治法)

    3. 对基准左右两侧的子数组分别进行递归排序。 4. 当子数组只剩一个元素时,排序结束。 在Java中,快速排序可以这样实现: ```java public class QuickSort { public static void quickSort(int[] arr, int low, ...

    用队列实现非递归排序

    总的来说,用队列实现非递归排序是一种可行的方法,虽然在某些情况下效率可能不高,但其简单易懂的逻辑和无递归的特点使其在特定场景下具有一定的价值。在设计和优化排序算法时,需要根据数据特性以及对时间和空间...

    JAVA快速排序(递归实现与非递归堆栈模拟实现)

    快速排序是一种非常高效的排序算法,它的基本思想是通过一趟排序将待排记录分隔成独立的两部分,其中一部分的所有记录都比另一部分的所有记录都要小,然后分别对这两部分继续进行排序,以达到整个序列有序的目的。...

    归并排序,消除递归归并排序,快排,Java实现

    Java中,可以使用`java.util.Arrays.sort()`方法,它内部实现了高效的排序算法,对于小数组可能会使用插入排序,大数组则使用了Timsort,一种结合了归并排序和插入排序优点的混合排序算法。 以上就是关于归并排序、...

    递归归并排序算法

    归并排序算法是基于分治策略的一种高效排序方法,其时间复杂度为O(n log n),空间复杂度为O(n)。该算法适用于大数据量的排序问题,在实际应用中有着广泛的应用场景。通过上述代码的分析,我们可以看到归并排序的具体...

    插入排序、选择排序、希尔排序、堆排序、冒泡、双向冒泡、快速排序、归并排序、递归的归并排序、基数排序

    4. 堆排序:堆排序是一种树形选择排序,通过构建最大堆或最小堆来实现排序。它的平均和最坏情况时间复杂度均为O(n log n)。 5. 冒泡排序:冒泡排序是最基础的排序算法之一,通过不断交换相邻的逆序元素,使得大元素...

    全排序算法c++递归实现

    给定的C++代码实现了全排列的一种递归方法。我们首先对代码进行逐行解读: 1. **包含头文件**:`#include &lt;iostream.h&gt;`。这里使用了较老的标准库文件,现代C++更推荐使用`#include &lt;iostream&gt;`。 2. **宏定义**...

Global site tag (gtag.js) - Google Analytics